Forum: FPGA, VHDL & Co. differentieller Manchester Encoder/Decoder in VHDL


von Tobias (Gast)


Lesenswert?

Hallo zusammen,
ich möchte selber einen differentiellen Manchester Encoder / Decoder in 
VHDL schreiben. Ich weiss, wie der differentielle Manchester Code 
funktioniert, aber irgendwie fehlt es mir jetzt grade an einem Ansatz, 
dies in VHDL umzumünzen :-( kann mir einer ein wenig auf die Sprünge 
helfen?

zumindest der Codierer ist doch eigentlich total einfach:

- wenn eine 1 kommt, den nicht invertierten Takt raus geben.
- wenn nochmal eine 1 kommt, den invertierten Takt raus geben.
- wenn eine 0 kommt, das selbe raus geben wie vorher.

Stimmt doch oder? Das klingt irgendwie einfach, aber hier:
http://electronicsinourhands.blogspot.ch/2012/10/manchester-encoder.html
wird (allerdings für einfachen Manchestercode) mit einem 
Zustandsautomaten gearbeitet, ich erkenne aber nicht wieso :-(


Dann noch der Decoder, irgendwie kann ich ja noch eine Taktrückgewinnung 
machen aus dem Manchester-Datenstrom... nur wie jetzt genau? hier fehlt 
mir der Ansatz komplett.

Ich hoffe ihr könnt mir ein paar Tipps geben :-)

Gruss

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


Lesenswert?

Tobias schrieb:
> aber irgendwie fehlt es mir jetzt grade an einem Ansatz, dies in VHDL
> umzumünzen
Wie würdest du den Coder mit Hardwarebausteinen aufbauen? Wenn du das 
kannst, dann ist es leicht, diese Hardware mit VHDL zu beschreiben 
(daher kommt der Begriff Hardwarebeschreibungssprache).

Tobias schrieb:
> wird mit einem Zustandsautomaten gearbeitet, ich erkenne aber nicht wieso
Weil du dir den vorherigen Zustand merken musst. Du selber schreibst 
ja:
> - wenn eine 1 kommt ...
> - wenn nochmal eine 1 kommt
Und um etwas zu "Merken" und auf ein "Vorher" reagieren zu können, ist 
ein Zustandsautomat (in welcher Form auch immer) nötig.

Das RC-5 Fernbedienungsprotokoll ist auch Manchestercodiert:
http://www.lothar-miller.de/s9y/categories/50-RC-5

von Tobias (Gast)


Lesenswert?

Hi Lothar,

also gewöhnliche Manchestercodierung ist einfach, da dürfte wohl ein XOR 
genügen, ja? aber in dem Link den ich noch gepostet habe, wird das 
wesentlich komplizierter gemacht, und ich verstehe nicht warum. Er 
benötigt da 5 verschiedene Zustände!

Wie sieht es denn beim Decoder aus, wie macht man es "richtig" ?
nehmen wir an ich möchte mit einem 50 MHz Clock arbeiten, dann kann ich 
mit einem gewöhnlichen FPGA wohl nicht "einfach so" eine klassische 
Flankendetektion machen mit

if input_old = '0' and input_new = '1' then
...
end if;

oder? jetzt mal gesetzt den Fall dass mein FPGA auch nur 50..100 MHz 
Clock hat. Ich bin noch ein wenig verwirrt :-)

Gruss:
Tobias

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


Lesenswert?

Tobias schrieb:
> Wie sieht es denn beim Decoder aus, wie macht man es "richtig" ?
Man sieht nach, ob in einem "Erwartungsfenster" eine Flanke kommt...
Tobias schrieb:
> nehmen wir an ich möchte mit einem 50 MHz Clock arbeiten
Also als "Manchestertakt"...
> mal gesetzt den Fall dass mein FPGA auch nur 50..100 MHz Clock hat.
Dann wird das nichts mit der Taktrückgewinnung ohne externe Bausteine. 
Denn der von mir verlinkte RC5 Decoder macht diese Aufsynchronisierung 
ja durch Überabtastung.
Aber prinzipiell ist es immer so, dass du nAch jeder Flanke ein (oder 
zwei) Fenster definierst, in denen was passieren muss. Und abhängig 
davon, was

Tobias schrieb:
> also gewöhnliche Manchestercodierung ist einfach, da dürfte wohl ein XOR
> genügen, ja?
Jein.
Ja, weil es tatsächlich so aussieht.
Und nein, weil in einem FPGA wird nichts mit einem Takt logisch 
verknüpft wird...

> aber in dem Link den ich noch gepostet habe, wird das wesentlich
> komplizierter gemacht, und ich verstehe nicht warum. Er benötigt da 5
> verschiedene Zustände!
Eigentlich nur 4 Zustände (und damit 2 FFs), denn der "Z"-Zustand ist 
ein Ausnahmezustand und "Kür"...

Aber dafür ist eben nicht mehr der Takt direkt mit dem Ausgangssignal 
kombinatorisch verknüpft. Und wenn du das mit deiner Art auch so machen 
willst, dann brauchst du 1 FF zum Speichern des vorherigen Wertes und 1 
FF zum Takten des Ausgangssignals. Und schon hast du gleich viele 
Ressourcen verbraucht...

von Tobias (Gast)


Lesenswert?

Hi Lothar,
danke für die Ausführungen. Das heisst also, dass ich meinen 
Manchesterencoder eigentlich mit mehr als 50 MHz laufen lassen muss 
(z.B. 100 MHz) und dann in dem Zustandsautomaten wirklich die Bits 
einzeln erzeuge, ohne mit dem Takt zu verXORen ja?

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


Lesenswert?

Tobias schrieb:
> ohne mit dem Takt zu verXORen ja?
Erst mal ja. Du könntest allerdings noch was mit DDR-FFs tricksen, die 
mit jeder steigenden 50MHz Flanke geladen werden, und dann mit der 
"fallenden" Flanke den jeweils anderen Wert ausgeben. Das wäre auch ein 
sauberes kalkulierbares Design...

Was willst du denn mit 50MHz biphase codieren?

von Tobias (Gast)


Lesenswert?

Ich habe nichts bestimmtes vor, ich will nur als Übung mal eine solche 
Übertragungsstrecke bauen zwischen zwei FPGA Evalboards. Der eine FPGA 
soll einen Datenstrom mit differentieller Manchestercodierung senden, 
dann will ich es über ein Kabel leiten und am zweiten FPGA-Board 
empfangen. Dann die Adern vertauschen und sich freuen, dass immer noch 
die richtigen Bits empfangen werden ;-) Es ist also ein reines 
Bastelprojekt. 50 MHz war einfach mal ein Wert, weil ich einen solchen 
Quarz auf dem Board habe. Man könnte auch 10 MHz oder wasauchimmer 
nehmen, das ist im Prinzip wurst....

Ich bin mir grade nochmals am Aufzeichnen als Timing Diagram, wie die 
Signale ausschauen sollen. Ich teile einfach den FPGA-Clock durch 2, 
also 50/2=25 MHz, dann ist mein "Manchesterclock" halt 25 MHz, und mit 
diesem Signal darf ich dann was logisch verknüpfen, oder? das wäre dann 
im Prinzip so eine Art "Datenclock" oder wie man es auch immer nennen 
möchte...

von Tobias (Gast)


Lesenswert?

Hi Lothar, ich nochmal :-)
bin mir grade einen Zustandsautomaten am überlegen für den Coder.

Kann es sein, dass es recht aufwendig wird? meiner hat jetzt 8 Zustände. 
Man muss eigentlich drei Dinge wissen:

- was für ein Bit habe ich zuletzt ausgegeben, 0 oder 1
- was für ein Bit soll ich jetzt ausgeben, 0 oder 1
- welche Flanken soll ich ausgeben, fallend oder steigend

und dann gibt es eigentlich 8 Zustände:

vorher 0, jetzt 0, fallend
vorher 0, jetzt 0, steigend
vorher 0, jetzt 1, fallend
vorher 0, jetzt 1, steigend
vorher 1, jetzt 1, fallend
vorher 1, jetzt 1, steigend
vorher 1, jetzt 0, fallend
vorher 1, jetzt 0, steigend

richtig? und diese 8 Zustände kann man dann in einen Automaten 
verbasteln... irgendwie.

von Tobias (Gast)


Lesenswert?

Hi Lothar,
 (liest du noch mit? :-))

ich habe mal was geschrieben, allerdings ohne "richtigen" 
Zustandsautomaten:
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
--------------------------------------------------------------------------------
6
7
entity machester is
8
  port(
9
    clk       : in  std_logic;
10
    reset     : in  std_logic;
11
    data_i    : in  std_logic;
12
    data_o    : out std_logic
13
  );
14
end machester;
15
16
--------------------------------------------------------------------------------
17
18
architecture rtl of machester is
19
  signal data_o_prev : std_logic;
20
  signal data_o_curr : std_logic;
21
begin
22
23
  data_o <= data_o_curr;
24
25
  o : process(clk, reset)
26
  begin
27
    if reset = '1' then
28
      data_o_prev <= '0';
29
      data_o_curr <= '0';
30
      
31
    elsif rising_edge(clk) then
32
      if data_i = '0' then
33
        if data_o_prev = '0' then
34
          data_o_curr <= '1';
35
        else
36
          data_o_curr <= '0';
37
        end if;
38
      else
39
        if data_o_prev = '0' then
40
          data_o_curr <= '0';
41
        else
42
          data_o_curr <= '1';
43
        end if;
44
      end if;
45
      
46
      data_o_prev <= data_o_curr;
47
      
48
    end if;
49
  end process;
50
  
51
end rtl;

allerdings kämpfe ich grade noch sehr mit ModelSim. Es sieht manchmal so 
aus, als ob es funtionieren würde, aber irgendwie doch nicht so 
richtig... Was mache ich falsch? wo in dem obigen Code liegt meine 
fehlüberlegung.

Gruss

von PittyJ (Gast)


Lesenswert?

Reicht die Sensitivity-Liste des Prozesses zum Simulieren?
Ich hatte damit letzens auch Probleme, dass ich ein paar Signale 
vergessen hatte. (Für die Synthese war es ja egal)

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


Lesenswert?

Das hier:
1
 if data_i = '0' then 
2
         if data_o_prev = '0' then 
3
           data_o_curr <= '1'; 
4
         else 
5
           data_o_curr <= '0'; 
6
         end if; 
7
       else 
8
         if data_o_prev = '0' then 
9
           data_o_curr <= '0'; 
10
         else 
11
           data_o_curr <= '1'; 
12
         end if; 
13
       end if;
Könntest du auch so schreiben:
1
data_o_curr <= data_i XNOR data_o_prev;

Tobias schrieb:
> Es sieht manchmal so aus, als ob es funtionieren würde, aber irgendwie
> doch nicht so richtig... Was mache ich falsch?
Ich tippe auf einen Takt Latency. Woher kommt das Datenbit? Wann 
wechselt das? Kannst du mal den Screenshot einer Waveform posten?

PittyJ schrieb:
> Reicht die Sensitivity-Liste des Prozesses zum Simulieren?
Ja, da ist alles drin: reset und clk

von Tobias (Gast)


Lesenswert?

Hallo Lothar,

ohje, dass ich das so vereinfacht schreiben könnte, darauf bin ich nicht 
gekommen :-)

Das mit dem einen Takt latency könnte stimmen. Ich kann es jetzt grad 
nicht anschauen, ich brauche jeweils circa 30 Minuten, bis ich Modelsim 
gestartet habe und wieder herausgefunden habe, wie man es bedient. (Ich 
benutze Altera Quartus II, dort ist jetzt nur noch Modelsim zum 
Simulieren vorhanden, früher ging es noch etwas einfacher mit dem Vector 
Waveform Simulator oder wie der hiess).

Gruss Tobias

von berndl (Gast)


Lesenswert?

Tobias schrieb:
> Ich kann es jetzt grad
> nicht anschauen, ich brauche jeweils circa 30 Minuten, bis ich Modelsim
> gestartet habe und wieder herausgefunden habe, wie man es bedient.

Modelsim startet man (imho) am besten mit einer .do Datei, also z.B.:
1
vsim -do tb_xyz.do
wobei tb_xyz.vhd eben auch der Name deiner Testbench ist. In der 
tb_xyz.do wuerde z.B. stehen:
1
# Kompilate landen im Subdirectory 'work'
2
vlib work
3
# Wir wollen 'work' fuer die Simulation verwenden
4
vmap work work
5
#
6
# Wir kompilieren alle Teile des Designs
7
vcom -explicit -93 "xyz.vhd"
8
vcom -explicit -93 "tb_xyz.vhd"
9
#
10
# Wir laden den gewuenschten Design in den Simulator
11
vsim -lib work tb_xyz
12
#
13
# evtl hier noch die gewuenschten Signale anzeigen lassen oder
14
# eine 'wave.do' im Simulator abspeichern
15
#
16
# run -all wuerde die Simulation laufen lassen (bis Ende/keine Clock)
17
#run -all
Deine Testbench musst du halt schreiben. Dafuer ist's dann immer wieder 
das gleiche um die Simulation zu starten...

von Tobias (Gast)


Lesenswert?

Hmm geht das auch wenn Modelsim mit Quartus zusammen installiert wurde? 
Irgendwie kann ich das gar nicht aufrufen aus einer Shell heraus, 
sondern ich kann es nur starten indem ich in Quartus ein Projekt öffne 
und dann "RTL Simulation" anklicke. Was mahce ich falsch? :-/

von berndl (Gast)


Lesenswert?

Tobias schrieb:
> Hmm geht das auch wenn Modelsim mit Quartus zusammen installiert wurde?
> Irgendwie kann ich das gar nicht aufrufen aus einer Shell heraus,
> sondern ich kann es nur starten indem ich in Quartus ein Projekt öffne
> und dann "RTL Simulation" anklicke. Was mahce ich falsch? :-/

evtl. fehlende Pfadangabe? "Damals" mit ModelSimXE (Xilinx) und "heute" 
mit ModelSimPE funktioniert das bei mir genau so. Mit Altera und deren 
verkrueppeltem ModelSim habe ich aber noch nix zu tun gehabt...
Hast du die o.g. Programme (vlib, vmap, vcom, vsim) bei dir auf der 
Platte liegen?

von Tobias (Gast)


Lesenswert?

In der Zwischenzeit habe ich rausgefunden wie es geht :-)
Hatte einen Pfad falsch angegeben. Jetzt läuft es!
Was allerdings nicht geht:

dun -all lässt ihn unendlich lange simulieren....

habe mal als einfachen Test folgendes in VHDL geschrieben:
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
--------------------------------------------------------------------------------
6
7
entity gatetest is
8
  port(
9
    clk : in std_logic;
10
    a, b : in std_logic;
11
    c : out std_logic
12
  );
13
end gatetest;
14
15
--------------------------------------------------------------------------------
16
17
architecture rtl of gatetest is
18
begin
19
20
  process(clk)
21
  begin
22
    if rising_edge(clk) then
23
      c <= a and b;
24
    end if;
25
  end process;
26
  
27
end rtl;

und dann die Testbench:
1
LIBRARY ieee;                                               
2
USE ieee.std_logic_1164.all;                                
3
4
ENTITY gatetest_vhd_tst IS
5
END gatetest_vhd_tst;
6
ARCHITECTURE gatetest_arch OF gatetest_vhd_tst IS
7
-- constants                                                 
8
-- signals                                                   
9
SIGNAL a : STD_LOGIC;
10
SIGNAL b : STD_LOGIC;
11
SIGNAL c : STD_LOGIC;
12
signal clk : std_logic;
13
COMPONENT gatetest
14
  PORT (
15
  clk : in std_logic;
16
  a : IN STD_LOGIC;
17
  b : IN STD_LOGIC;
18
  c : OUT STD_LOGIC
19
  );
20
END COMPONENT;
21
BEGIN
22
  i1 : gatetest
23
  PORT MAP (
24
-- list connections between master ports and signals
25
  clk => clk,
26
  a => a,
27
  b => b,
28
  c => c
29
  );
30
init : PROCESS                                               
31
-- variable declarations                                     
32
BEGIN                                                        
33
  clk <= '0';
34
  wait for 10 ns;
35
  clk <= '1';
36
  wait for 10 ns;
37
END PROCESS init;       
38
39
                                   
40
always : PROCESS                                              
41
-- optional sensitivity list                                  
42
-- (        )                                                 
43
-- variable declarations                                      
44
BEGIN                                                         
45
46
  a <= '0';
47
  b <= '0';
48
  wait for 100 ns;
49
  
50
  a <= '0';
51
  b <= '1';
52
  wait for 100 ns;
53
  
54
  a <= '1';
55
  b <= '1';
56
  wait for 100 ns;
57
  
58
  a <= '1';
59
  b <= '0';
60
  wait for 100 ns;
61
  
62
WAIT;                                                        
63
END PROCESS always;                                          
64
END gatetest_arch;

und das sim file:
1
# Kompilate landen im Subdirectory 'work'
2
vlib work
3
# Wir wollen 'work' fuer die Simulation verwenden
4
vmap work work
5
#
6
# Wir kompilieren alle Teile des Designs
7
vcom -explicit -93 "gatetest.vhd"
8
vcom -explicit -93 "tb_gatetest.vhd"
9
#
10
# Wir laden den gewuenschten Design in den Simulator
11
vsim -lib work gatetest_vhd_tst
12
#
13
# evtl hier noch die gewuenschten Signale anzeigen lassen oder
14
# eine 'wave.do' im Simulator abspeichern
15
#
16
# run -all wuerde die Simulation laufen lassen (bis Ende/keine Clock)
17
#run -all
18
19
add wave -position insertpoint  \
20
sim:/gatetest_vhd_tst/a \
21
sim:/gatetest_vhd_tst/b \
22
sim:/gatetest_vhd_tst/c \
23
sim:/gatetest_vhd_tst/clk

mit run -all simuliert er einfach bis zum geht nicht mehr, obwohl die 
Signale schon lange sich nicht mehr ändern aufgrund des wait Statement. 
Aber der clock clockt natürlich noch immer, weiss nicht obs da dran 
liegt...

von berndl (Gast)


Lesenswert?

du musst halt in deinem eigentlichen Simulationsprozess am Ende ein 
Statement ala
1
end_of_sim <= '1';
haben, und dann in deinem Taktgenerierungsprozess sowas wie
1
   clk_process : process
2
   begin
3
      wait for clk_period*5;
4
    loop
5
      clk50 <= '1';
6
      wait for clk_period/2;
7
      clk50 <= '0';
8
      wait for clk_period/2;
9
      exit when end_of_sim = '1';
10
    end loop;
11
    wait;
12
   end process;
Du musst also in der TB explizit alle Takte stoppen. Dann haelt auch der 
Simulator automatisch an...

von P. K. (pek)


Lesenswert?

berndl schrieb:
> Du musst also in der TB explizit alle Takte stoppen. Dann haelt auch der
> Simulator automatisch an...

Oder du kippst was in dieser Art an geeigneter Stelle rein:
1
    assert false
2
      report " ===== Verification run 'TESTBENCH' finished ====="
3
      severity failure;

Dann ist es egal, wenn irgendwo noch ein Clock aktiv sein sollte.

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.