Forum: FPGA, VHDL & Co. Probleme bei Synthese durch parallele Blöcke?


von Martin (Gast)


Lesenswert?

Hallo!

Ich will eine Alarmsteuerung entwerfen.
Die Steuerung hat 5 Sensoren (3x Chemie Alarm,2x Feuer Alarm).
Das ganze soll über einen Zustandsautomat gebaut werden. Im Prinzip 
ist das kein Problem, ich wollte das so machen:

Ich habe 2 getrennte Blöcke. Dieser Block...

current_state=next_state;
always@(posedge clock)
begin
case(current_state)
...
end

...bearbeitet mir den aktuellen Zustand.

Der zweite Block hier, berechnet mir den Folgezustand und reagiert auf 
Änderungen der Sensoren.

always@(sens_f1,sens_f2,sens_c1,sens_c2,sens_c3)
begin
case({sens_f1,sens_f2,sens_c1,sens_c2,sens_c3})
..
end

Nun ist für die Aufgabe gefordert, dass je nach Alarm unterschiedliche 
Dinge passieren sollen (z.B. soll die Firmenleitung gerufen werden und 
evakuiert werden) das wird über 8 Leds angezeigt. Eine davon simuliert 
eine Sirene und blinkt in einem bestimmten Takt.
Hier kommt auch mein Problem:
Ich habe gehört dass man wenn man zwei Blöcke baut wie oben, die ja 
parallel synthetisiert werden, man in beiden nicht auf gleiche Variablen 
zugreifen kann. Ohne das, weiß ich aber nicht wie ich die Aufgabe lösen 
kann, denn ich wollte das blinken so lösen:

Ich gehe in Block 1 in den entsprechenden Zustand. Dort setze ich die 
led Leiste erstmal, z.B. so: leds = 'b0010_1100 (was das jetzt bedeutet 
ist egal).
Das blinken wollte ich mit einer xor Verknüpfung realisieren z.B. so:
leds = leds ^ 'b0000_0001 (lässt Led 8 blinken), dafür habe ich einen 
timer gebaut.
Wenn ich aber jetzt nach einem Takt wieder in den Zustand komme, 
überschreibt mir ja leds = 'b0010_1100 wieder meine gerade gesetzte 1 
bei Led 8. Also hatte ich mir überlegt ich baue einen lock und zwar so:

if(lock==0)
leds='b0010_1100;
else
begin
 lock=1;
 timer_32=timer_32-1;
 if(timer_32==0)
   begin
   leds = leds ^ 'b0000_0001;
   timer_32=40000000; (40.000.000 da 40 MHz und ich alle 1 Sekunde 
umschalten will)
  end
end

Damit ich das so machen kann, müsste ich aber in Block 2 lock auf 0 
setzen.
Somit würde ich aber lock in beiden Blöcken verändern. Geht das? Oder 
knallts dann in der Synthese?

Ich hoffe ihr seht hier durch!
Ich wäre für Hilfe sehr dankbar!

von Jan M. (mueschel)


Lesenswert?

Nein, das geht nicht, Signale koennen nur in genau einem Block 
geschrieben werden, wie du richtig geschrieben hast.

Setze doch in deiner Steuerung ein Bit, ob die LED blinken soll oder 
nicht. In einem weiteren Prozess (oder Block, ich kenne Verilog nicht) 
kannst du dann die LED ansteuern auf Basis dieses bits.

von Martin (Gast)


Lesenswert?

Hallo Jan!

Danke für deine Antwort! :)

Aber genau das wollte ich ja mit lock machen. lock ist nur was internes 
und wird nicht nach außen geführt (reg lock). Ich wollte lock im 
Steuerblock immer auf 0 setzen und im jeweiligen Zustand dann später auf 
1.

von Jan M. (mueschel)


Lesenswert?

Ich meinte das so: led_blink wird nur im ersten Block gesetzt, die led 
nur im zweiten Block geschaltet. Die anderen, nicht blinkenden LED 
kannst du ja weiterhin direkt in Block1 ansteuern.

Block1:

led_blink <= 0
if error_condition
   led2_blink <= 1



Block2:

if led2_blink = 1
  if counter = 40Mio
    led2 = not led2

von Günter -. (guenter)


Lesenswert?

Martin wrote:
...
> Hier kommt auch mein Problem:
> Ich habe gehört dass man wenn man zwei Blöcke baut wie oben, die ja
> parallel synthetisiert werden, man in beiden nicht auf gleiche Variablen
> zugreifen kann. Ohne das, weiß ich aber nicht wie ich die Aufgabe lösen
> kann, denn ich wollte das blinken so lösen:
>

Der Ansatz mit den 2 Blöcken für die State Machine ist ganz gut. Dann 
mit dem Lock wird es sehr verzwickt.

Aus der State Machine hast du ja schon den leds Vektor, bei dem ein Bit 
Anzeigt das die eine LED blinken soll.

Mein Vorschlag währe einen dritten always block zu kreieren der immer 
den Takt für das Blinken erzeugt, unabhängig von der State Machine.

Dann nimmst du einen kombinatorischen Konstrukt:
1
assign led_blink = blink_takt & leds[x];

Das x spezifiziert das Bit für die blinkende LED im leds Vektor. Der 
blink_takt kommt von dem dritten always Block mit dem Takt. Jedes mal 
wenn deine State Machine jetzt sagt die LED soll blinke, dann wird das 
Signal mit dem Takt UND-verknüpft und schon blinkt deine LED.

Damit musst du dir keine Gedanken über irgendwelche Locks machen.

Gruß,

Günter

von Martin (Gast)


Lesenswert?

@Jan
Ich glaube ich hab zu der Aufgabenstellung was ausgelassen. Im Prinzip 
weiß ich schon wenn ich in Block2 bin, dass geblinkt werden soll, da das 
implizit durch den jeweiligen Zustand vorgegeben ist. Somit brauche ich 
in Block 1 nicht mehr zu sagen dass geblinkt werden soll.
Bei mir sah das bis jetzt so aus:

Block1:

lock=0;
next_state=tue_irgendwas_und_blinke;

Block2:

tue_irgendwas_und_blinke:
begin
  if(lock==0)
   begin
     lock=1;
     leds='b0010_1100;
   end
  if(lock==1)
   begin
     timer_32=timer_32-1;
     if(timer_32==0)
      begin
        leds=leds^'b0000_0001;
        timer_32=40000000;
      end
   end

Aber im Prinzip kann man den lock ja ganz weglassen und nur den Timer 
überprüfen, also so:

Block1:

next_state=tue_irgendwas_und_blinke;

Block2:

leds[0:7]='b0010_110; //Geht das?
  if(timer_32==40000000)
    leds[8]=0;
  if (timer_32>0)
    timer_32=timer_32-1;
  if(timer==0)
    begin
      leds[8]=leds[8]^1;
      timer_32=40000000;
    end

Dann fällt zwar der lock raus, aber es gibt das Problem, dass ich wieder 
nur in Block 2 timer_32 setzen kann und ich habe das gleiche Problem wie 
ich es mit lock hätte. (Oder übersehe ich was?)

@Günter
Mit deiner Lösung müsste es gehen.
Also würde ich einen dritten Block machen, der so aussieht

always@(posedge clock)
begin
  if(timer_32==40000000)
    blink_takt=0;
  if (timer_32>0)
    timer_32=timer_32-1;
  if(timer==0)
    begin
      blink_takt=1;
      timer_32=40000000;
    end
end


Block 2 würde dann so aussehen:
always@(posedge clock)
next_state=current_state;
begin
 case(...)

 tue_irgendwas_und_blinke:
 begin
  leds[0:7]='b0010_110; //Geht das?
  leds[8] = blink_takt;
 end
end

Das sollte doch gehen?!

von Günter -. (guenter)


Lesenswert?

Martin wrote:
...
>
> @Günter
> Mit deiner Lösung müsste es gehen.
> Also würde ich einen dritten Block machen, der so aussieht
>
> always@(posedge clock)
> begin
>   if(timer_32==40000000)
>     blink_takt=0;
>   if (timer_32>0)
>     timer_32=timer_32-1;
>   if(timer==0)
>     begin
>       blink_takt=1;
>       timer_32=40000000;
>     end
> end
>

Dein blink_takt wird hier nur für eine clock Periode aktive sein. Da 
wirst du nicht viel von der LED sehen.

Besser währe vielleicht den blink_takt aus dem always heraus zu nehmen 
und folgendes zu machen:
1
assign blink_takt = (timer_32 < 20000000);

Wenn jetzt timer_32 kleiner 20000000 ist, dann ist der Vergleich wahr 
und damit blink_takt 1. Wenn es größer/gleich 20000000 dann falsch und 
blink_takt ist 0. Damit erhälst du einen symmetrischen blink_takt.

von Martin (Gast)


Lesenswert?

Hallo!

Das stimmt, dort müsste bei mir statt blink_led=1, blink_led = ~ 
blink_led stehen oder ich mach es so wie du.

Jetzt verbleibt für mich aber noch ein Problem. Ich brauche noch einen 
Alarmnachlaufzeit. Auch wenn die Sensoren z.B. kein Feuer mehr melden, 
soll der Alarm 3 Minuten nachlaufen. Wie ich einen Timer für 3 Minuten 
mache ist kein Problem, ich mache zum 1s Timer einfach noch eine der bis 
180 zählt.
Aber wie kann ich den Timer überhaupt von außen zurücksetzen?
Ich wollte das über ein zusätzliches Signal machen, z.b. timer_reset.
Aber ich stosse wieder auf das gleiche Prolbem wie bei den LEDs:
Wie kann ich timer_reset auslösen und dann wieder zurücksetzen?
Das müsste ich ja im Steuerblock machen, der ist aber nur 
ereignisgesteuert (Ausschnit vom Steuerblock):

always@(sens_f1, sens_f2, sens_c1, sens_c2, sens_c3)
begin
   case({sens_f1,sens_f2,sens_c1,sens_c2,sens_c3})
   .....
       'b1?1??:
        begin
            //beide Feuersensoren sind aktiv
            if(sens_f2)
            begin
               next_state=f2_chem_s3;
            end

            //nur ein Feuersensor ist aktiv
            else
               begin
                  activate_sprinkler=1;
                  next_state=f1_chem_s3;
               end
        end
        ....
        'b00111:
        begin
            next_state=c3_s3;
        end
       ....
  endcase
end

Ich müsste ja aber dort irgendwo mein timer_reset Signal triggern (Ich 
kan n es zwar auf 1 setzen, ich bekomme es ja nicht mehr auf 0) Im 
Prinzip immer genau dann, wenn ich von einem Zustand mit Alarmsignal, in 
einen Zustand ohne Alarmsignal wechsel, dazu müsste ich ja nur 
current_state mit next_state vergleichen. Ich brauche dann auch 2 Timer 
für 3 Minuten, da einmal der Chemie- und einmal der Feueralarm 
nachlaufen kann.

von Günter -. (guenter)


Lesenswert?

Martin wrote:
> Hallo!
>
> Das stimmt, dort müsste bei mir statt blink_led=1, blink_led = ~
> blink_led stehen oder ich mach es so wie du.
>
> Jetzt verbleibt für mich aber noch ein Problem. Ich brauche noch einen
> Alarmnachlaufzeit. Auch wenn die Sensoren z.B. kein Feuer mehr melden,
> soll der Alarm 3 Minuten nachlaufen. Wie ich einen Timer für 3 Minuten
> mache ist kein Problem, ich mache zum 1s Timer einfach noch eine der bis
> 180 zählt.
> Aber wie kann ich den Timer überhaupt von außen zurücksetzen?
> Ich wollte das über ein zusätzliches Signal machen, z.b. timer_reset.
> Aber ich stosse wieder auf das gleiche Prolbem wie bei den LEDs:

Du solltest die Steuerung von den LEDs, speziell der blinkenden nicht 
mit der State Machine verknüpfen.

Die State Machine sollte dir nur ein Signal geben, dass der Alarm 
anliegt. Basierend auf diesem Signal kannst du jetzt extra Logik 
hinzufügen um die LED blinken zu lassen. Jedes mal wenn du das Blinken 
mit in die State Machine zurück bringst wird diese komplizierter.

...
>
> Ich müsste ja aber dort irgendwo mein timer_reset Signal triggern (Ich
> kan n es zwar auf 1 setzen, ich bekomme es ja nicht mehr auf 0) Im
> Prinzip immer genau dann, wenn ich von einem Zustand mit Alarmsignal, in
> einen Zustand ohne Alarmsignal wechsel, dazu müsste ich ja nur
> current_state mit next_state vergleichen. Ich brauche dann auch 2 Timer
> für 3 Minuten, da einmal der Chemie- und einmal der Feueralarm
> nachlaufen kann.

Die State Machine signalisiert nur das der Alarm anliegt. Der 
Nachlauftimer wird von dem aktiven Alarm gehalten und hat einen aktiven 
Ausgang. Der Ausgang wird jetzt mit deinem Blink-Takt-Geber 
UND-Verknüpft.

Sollte der Alarm abfallen, läuft der Nachlauftimer los und zählt die 3 
Minuten runter. In der ganzen Zeit ist der Ausgang immer noch aktiv.

Da du scheinbar mehrere Nachlauftimer benötigst, macht es vielleicht 
Sinn ein extra Modul davon zu machen und es entsprechend oft zu 
instanzieren.

von Martin (Gast)


Lesenswert?

Hallo!

Ersteinmal Danke für deine Geduld und Hilfe!

Entweder mir ist nicht ganz klar wie du das meinst oder ich habe einfach 
zu wenig Wissen in Sachen Verilog oder ich stelle mich einfach furchtbar 
dämlich an.

Wenn ich also in einen Zustand gehe, wo es brennt, so führe ich folgende 
Anweisung im Zustand aus:

melde_feuer=1;

Wenn ich dann in einen Zustand wechsel, wo es nicht brennt, wir daraus:

melde_feuer=0;

Die Erkennung für die Negative Flanke von melde_feuer:

always@(posedge melde_feuer, negedge melde_feuer)
begin
//negedge melde_feuer
if (melde_feuer==0) nachlauf_feuer=1;
//posedge melde_feuer
if (melde_feuer==1) nachlauf_feuer=0;
end

Der Block für den Timer, das schalten der Ausgänge:

alway@(clock)
begin
    if(timer_init)
    begin
       timer_32=40000000; //für 40 MHz
       half_sec_blink=0;
       full_sec_blink=0;
       timer_init=0;
    end
    else
    begin
    if(timer_32>0)
    begin
     timer_32=timer_32-1;
     if(timer_32==20000000) //Hälfte der Zeit
       half_sec_blink=~half_sec_blink;
    end

    if(timer_32==0)
    begin
       half_sec_blink=~half_sec_blink;
       full_sec_blink=~full_sec_blink;
       timer_32=40000000;

       //Problemstelle:
       if(nachlauf_feuer==0)
       timer_8_feuer=180;
       if(nachlauf_feuer==1)
       begin
         if(timer_8_feuer==0) nachlauf_feuer=0; //Geht so ja nicht!
         else timer_8_feuer=timer_8_feuer-1;
       end
    end

//Ausgänge schalten:
//Sobald nachlauf_feuer==0 und melde_feuer=0 ist Ausgang Null
//ansonsten blink Ausgang im Sekundentakt
ausgang_feuer_sirene=(nachlauf_feuer||melde_feuer)&blink_takt_sekunde;

end
end


Das Problem (Problemstelle ist gekennzeichnet):
Hier schalte ich ja aber nachlauf_feuer wieder in 2 Blöcken.

Also müsste es doch aber so hier gehen:
(Ich habe nur das Gefühl es geht irgendwie einfacher und ich sehs 
einfach nicht)

Die Erkennung für die Negative Flanke von melde_feuer:

always@(posedge restart_timer,posedge melde_feuer, negedge melde_feuer)
begin
if (restart_timer_8_feuer==1) nachlauf_feuer=0;
if (melde_feuer==1) nachlauf_feuer=1;
if (melde_feuer==0) nachlauf_feuer=0;
end

Der Block für den Timer, das schalten der Ausgänge:

alway@(clock)
begin
    if(timer_init)
    begin
       timer_32=40000000; //für 40 MHz
       half_sec_blink=0;
       full_sec_blink=0;
       timer_init=0;
    end
    else
    begin
    if(timer_32>0)
    begin
     timer_32=timer_32-1;
     if(timer_32==20000000) //Hälfte der Zeit
       half_sec_blink=~half_sec_blink;
    end

    if(timer_32==0)
    begin
       half_sec_blink=~half_sec_blink;
       full_sec_blink=~full_sec_blink;
       timer_32=40000000;

       //Problemstelle:
       if(nachlauf_feuer==0)
       begin
          timer_8_feuer=180;
          restart_timer_8_feuer=0;
       end
       if(nachlauf_feuer==1)
       begin
         if(timer_8_feuer==0) restart_timer_8_feuer=1;
         else timer_8_feuer=timer_8_feuer-1;
       end
    end

//Ausgänge schalten:
//Sobald nachlauf_feuer==0 und melde_feuer=0 ist Ausgang Null
//ansonsten blink Ausgang im Sekundentakt
ausgang_feuer_sirene=(nachlauf_feuer||melde_feuer)&blink_takt_sekunde;

end
end

von Günter -. (guenter)


Lesenswert?

Martin wrote:
...
>
> Wenn ich also in einen Zustand gehe, wo es brennt, so führe ich folgende
> Anweisung im Zustand aus:
>
> melde_feuer=1;
>
> Wenn ich dann in einen Zustand wechsel, wo es nicht brennt, wir daraus:
>
> melde_feuer=0;
>
> Die Erkennung für die Negative Flanke von melde_feuer:
>
> always@(posedge melde_feuer, negedge melde_feuer)

Ich weiß nicht ob du das in synthesierbarer Logik machen kannst. Immer 
wenn du posedge oder negedge nimmst dann wird in der Logik ein Register 
genommen. Der Clock-Eingang eines Registers sollte aber immer mit dem 
synchronen Takt belegt werden. Was du hier also kreierst ist ein Latch, 
aber ich glaube nicht das es funktioniert den auf beide Flanken zu 
triggern. Schau dir den Synthesebericht bezüglich Warnungen mit den 
Signalen mal an.

> begin
> //negedge melde_feuer
> if (melde_feuer==0) nachlauf_feuer=1;
> //posedge melde_feuer
> if (melde_feuer==1) nachlauf_feuer=0;
> end

Meiner Ansicht nach brauchst du das auch gar nicht. Mit melde_feuer hast 
du ja schon ein signal das den Alarm signalisiert. Das Signal nimmst du 
jetzt um den Nachlauftimer zu laden. Immer wenn melde_feuer aktiv ist, 
wird der Nachlauftimer mit den max Wert geladen. Wenn melde_feuer 
inaktive wird, fängt der Nachlauftimer an runter zu zählen.

Das ganze sieht ungefähr so aus. Ziehe den timer_8_feuer aus den 
timer_32 heraus und packe ihn in einen neuen always block:
1
always@(posedge clock)
2
  if(melde_feuer)
3
   timer_8_feuer = 180;
4
  else
5
   if(timer_32 == 0 && timer_8_feuer > 0)
6
    timer_8_feuer = timer_8_feuer - 1;

Das eigentliche Signal wird jetzt aus kombinatorischer Logik gebildet:
1
assign nachlauf_feuer = (timer_8_feuer > 0);
2
assign ausgang_feuer_sirene = machlauf_feuer & blink_takt_sekunde;

Wenn timer_8_feuer > 0 ist dann ist das Ergebnis 1 und wird 
nachlauf_feuer zugewiesen. Wenn er 0 ist dann ist der Vergleich 0 und 
wird nachlauf_feuer zugewiesen.

Um die LED blinken zu lassen wird das nachlauf_feuer mit dem 
blink_takt_sekunde UND verknüpft.

Versuche sequentielle Blöcke, also always-Blöcke mit @(posedge clock) 
nur eine Funktion ausführen zu lassen. Dann verbinde solche sequentielle 
Blöcke mit kombinatorischer Logik.

von Martin (Gast)


Lesenswert?

@Günter
So hats letzendlich funktioniert. :)
Vielen Dank für deine Hilfe!

Gruß,
Martin

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.