Hallo Leute,
ich habe nun auch mal einen kleinen UART geschrieben. Er funktioniert
leider nicht ganz. Er empfängt recht häufig falsche Zeichen. Das
"falsche" Zeichen äußert sich in einem bit-versatz bzw. er "dichtet"
sich ein paar bits dazu.
Es hängt allerdings auch vom Zeichen selbst ab. Wenn ich z.b. ein 'h'
sende (binär : 01101000) empfängt er es immer richtig. Ein 'a' hingegen
(binär : 01100001) wird fast immer falsch empfangen.
Wenn jemand Lust und Zeit hat kann er sich ja gerne mal die Komponente
antun.
hier mal der quellcode des empfängers.
Wenn jemand eine idee hat warum es hapert, bzw. anderweitige
verbesserungsvorschläge hat, immer her damit :-)
--------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- Uncomment the following lines to use the declarations that are
-- provided for instantiating Xilinx primitive components.
--library UNISIM;
--use UNISIM.VComponents.all;
entity uart_rx is
port (clk : in std_logic;
res : in std_logic;
rx_in : in std_logic;
baud_clk_16 : in std_logic;
busy : out std_logic;
out_buffer : out std_logic_vector (7 downto 0);
trg : out std_logic);
end uart_rx;
architecture behave of uart_rx is
signal state : std_logic_vector (3 downto 0);
signal count : std_logic_vector (3 downto 0);
signal baud_clk_16_z1 : std_logic;
signal rx_buffer : std_logic_vector (7 downto 0);
signal err : std_logic;
signal trg_out : std_logic;
signal rx_in_z1 : std_logic;
signal rx_in_z2 : std_logic;
signal rx_in_z3 : std_logic;
--signal rx_in_z4 : std_logic;
begin
trg <= trg_out;
process (clk,res, baud_clk_16_z1, baud_clk_16, state, count, rx_in_z3,
rx_in_z2, rx_in_z1, rx_in, rx_buffer)
begin
if res = '1' then
state <= (others => '0');
count <= (others => '0');
rx_buffer <= (others => '0');
baud_clk_16_z1 <= '0';
err <= '0';
trg_out <= '0';
elsif clk'event and clk = '1' then
baud_clk_16_z1 <= baud_clk_16;
--rx_in_z4 <= rx_in_z3;
if baud_clk_16_z1 = '0' and baud_clk_16 = '1' then
rx_in_z1 <= rx_in ;
rx_in_z2 <= rx_in_z1;
rx_in_z3 <= rx_in_z2;
count <= count + '1';
if state = X"0" then
if rx_in_z3 = '0' then
--count <= count + '1';
if count = X"F" then
state <= X"1";
--count <= (others => '0');
end if;
else
count <= (others => '0');
end if;
else
if count = X"8" then
case state is
when X"1" => rx_buffer (0) <= rx_in_z3; state <= X"2";
when X"2" => rx_buffer (1) <= rx_in_z3; state <= X"3";
when X"3" => rx_buffer (2) <= rx_in_z3; state <= X"4";
when X"4" => rx_buffer (3) <= rx_in_z3; state <= X"5";
when X"5" => rx_buffer (4) <= rx_in_z3; state <= X"6";
when X"6" => rx_buffer (5) <= rx_in_z3; state <= X"7";
when X"7" => rx_buffer (6) <= rx_in_z3; state <= X"8";
when X"8" => rx_buffer (7) <= rx_in_z3; state <= X"9";
when X"9" => if rx_in_z3 = '1' then
out_buffer <= rx_buffer;
err <= '0';
trg_out <= '1';
else
out_buffer <= (others => '0');
err <= '1';
end if;
state <= X"0";
when others => NULL;
end case;
end if;
end if;
end if;
if trg_out = '1' then
trg_out <= '0';
end if;
end if;
end process;
end architecture;
-----------------------------------------------------------------------
gruß
rene
> if state = X"0" then> if rx_in_z3 = '0' then> --count <= count + '1';> if count = X"F" then> state <= X"1";> --count <= (others => '0');> end if;> else> count <= (others => '0');> end if;
Erstze das mal durch
if state = X"0" then
if rx_in_z3 = '0' then
count <= "0000";
state <= X"1";
end if;
MfG
Falk
danke für den tip. werde es heute abend mal ausprobieren.
obwohl mir noch nicht so ganz klar ist warum es daran liegen könnte. ich
muß ja erst ein rx-bit (16x baud_clk_16) abwarten nachdem ich die 0
erkannt habe, oder sehe ich da was falsch ?!
gruß
rene
Hallo Falk,
ich habs mal ausprobiert, aber es funktioniert leider nicht.
Mir ist nicht so ganz klar wieso das mit dieser Änderung laufen könnte.
Ich muß doch erst mein Start-Bit abwarten (also 16 baud_clk_16 pulse
zählen), bevor ich die eigentlichen nutzdaten in mein register packe ?
oder meintest du das so, das man noch einen zusätzlichen zustand
einfügen muß für das start-bit ?
gruß
rene
@TheMason
>Ich muß doch erst mein Start-Bit abwarten (also 16 baud_clk_16 pulse>zählen), bevor ich die eigentlichen nutzdaten in mein register packe ?
Ja. Aber mit der fallenden Flanke von rx_in_z3 musst du deinen Zähler
rücksetzen. Danach läuft er frei durch.
>oder meintest du das so, das man noch einen zusätzlichen zustand>einfügen muß für das start-bit ?
Ja.
MfG
Falk
nach Erkennung des Startbits musst du 1,5 bitzeiten bis zur Abtastung
des ersten Bits warten, damit der Pegel auch in der Mitte der Bits
abgefragt wird. Ansonsten fragst Du die Bits immer am Anfang ab und das
kann schon mal Fehler geben.
Gruß Jörg
Eines der Hauptprobleme, die es mit FPGAs gibt, sind asynchrone Signale
von aussen. Die MÜSSEN immer erst mal synchronisiert werden (ich hätte
auch schreiben können: die SOLLTEN immer erst mal synchronisiert werden,
aber aus der alltäglichen Erfahrung ist ein zwingendes MUSS immer
besser).
Denn ein asynchrones Signal geht z.B. auf mehrere LUTs/FFs im FPGA und
kommt da aufgrund von Laufzeitunterschieden nicht bei allen gleichzeitig
an. Daher weiß das halbe FPGA dann schon von dem neuen Zustand, die
andere Hälfte bekommt die Änderung erst zum nächsten Takt mit.
Und dann gibt es solche Effekte, dass das Design so ein paar Minuten
oder Zeichen lang gut funktioniert, dann gehts mal schief, dann
funktionierts wieder ne Zeit lang usw. usf.
Du hast gleich zwei solcher Signale in deinem Design:
rx_in : in std_logic;
baud_clk_16 : in std_logic;
Schau dir das mal genauer an.
Irgendwo dort liegt der Hund begraben (garantiert!).
Als Anhang habe ich ein kampferprobtes SIO-Design, schau dir mal die
Entprellung des RX-Signals an (so gehts, auch garantiert!).
BTW: die sensitivity-List des Process braucht nur zwei Signale:
process (clk,res)
Denn eine Änderung irgendeines anderen Signals aus deiner Liste führt
nicht zu einer Ausgangs- oder Signaländerung. Das ist aber eh' nur für
die Simulation interessant (reduziert die Simulationszeit), der
Synthesizer schmeisst die Dinger sowieso weg.
Gruß Lothar
@lothar
aber durch:
> elsif clk'event and clk = '1' then> baud_clk_16_z1 <= baud_clk_16;>> --rx_in_z4 <= rx_in_z3;>> if baud_clk_16_z1 = '0' and baud_clk_16 = '1' then>> rx_in_z1 <= rx_in ;> rx_in_z2 <= rx_in_z1;> rx_in_z3 <= rx_in_z2;
wird rx_in doch einsynchronisiert (es wird sonst nirgends das signal
rx_in verwendet !). außerdem ist baud_clk_16 auch synchron zum clk (da
dieser von clk abgeleitet wird).
oder sehe ich da was falsch ?!
gruß
rene
ps. danke für deine rs232-komponente. werde mir diese mal genauer
anschauen. ist ja bald wochenende :-))
@TheMason
>wird rx_in doch einsynchronisiert (es wird sonst nirgends das signal>rx_in verwendet !). außerdem ist baud_clk_16 auch synchron zum clk (da>dieser von clk abgeleitet wird).>oder sehe ich da was falsch ?!
Du siehst das vollkommen richtig. Alles im grünen Bereich.
Dein Problem liegt höchstwahrscheinlich in der State Machine.
MFG
Falk
ich glaub mir schwant was böses ...
ich habe vermutung das ihr beide recht habt.
mir ist nämlich gerade aufgefallen, das :
> if baud_clk_16_z1 = '0' and baud_clk_16 = '1' then>> rx_in_z1 <= rx_in ;> rx_in_z2 <= rx_in_z1;> rx_in_z3 <= rx_in_z2;
zwar meinen rx_in einsynchronisiert, ABER : eben nur mit dem baud_clk_16
und nicht mit meinem systemtakt clk. dadurch kann natürlich meine
statemachine nicht richtig funktionieren. schmärz lass nach ...
es ist denke ich ein problem des einsynchronisierens (falscher sync-clk)
und der statemachine (durch zu langsam einsynchronisiertes signal)
kann es sein das es das ist ? (ich muß das gleich zuhause mal testen)
gruß
rene
@TheMason
>mir ist nämlich gerade aufgefallen, das :>zwar meinen rx_in einsynchronisiert, ABER : eben nur mit dem baud_clk_16>und nicht mit meinem systemtakt clk. dadurch kann natürlich meine
Das ist schon OK, deine State Machine reagiert ja auch nur im
baud_clk_16 Takt, da müssen die Eingangssignale nicht schneller
abgetastet werden.
>es ist denke ich ein problem des einsynchronisierens (falscher sync-clk)>und der statemachine (durch zu langsam einsynchronisiertes signal)>kann es sein das es das ist ? (ich muß das gleich zuhause mal testen)
Hier ist das kein Problem.
MFG
Falk
@lothar
also ich habe mir mal deine einsychronisierung in meinen uart eingebaut.
(um rx_in zu entprellen). es funktioniert. wobei mir noch nicht so ganz
klar ist warum dem so ist. ob ich nun mit baud_clk_16 einclocke oder mit
dem systemtakt sollte für den weiteren verlauf der statemachine doch
unerheblich sein, oder ? und da das signal ja mehrfach abgetastet wird
dürfte es doch spätestens nach dem 4. takt sauber anliegen. das einzige
was ich bei meiner lösung nicht gemacht habe das ich mein rx-input aus
allen 4 flip-flops generiere, sondern nur aus 2 flip-flops, und dies
auch nur für die startbedingung (fallende flanke an rx).
ich wollte außerdem fragen ob ich dein einsynchronisieren so übernehmen
und öffentlich verwenden darf. ich würde meine komponente gerne ausbauen
und hier in der Codesammlung "verewigen". ist das mit einem
entsprechenden vermerk auf dich ok ?
gruß
rene
@TheMason
>ich wollte außerdem fragen ob ich dein einsynchronisieren so übernehmen>und öffentlich verwenden darf. ich würde meine komponente gerne ausbauen
Ersten sehe ich keine Code und zweites gibt darauf kein Patent ;-)
>und hier in der Codesammlung "verewigen". ist das mit einem>entsprechenden vermerk auf dich ok ?
Wen meinst du mit "dich?". Das Entprellen ist prinzipiell nicht falsch,
wird aber meist nicht gebraucht. Die Abtastung mit baud_clk_16 wirkt
schon entprellend genug. Ausserdem verwenden alle professionellen UARTs
auch nur die mit baud_clk_16 abgetasteten Werte (und bilden die
Mehrheitseintscheidung aus Abtastwert 7,8 und 9. Wenn du dann noch das
Startbit sowie Stopbit prüfst ist wasserdicht.
MfG
Falk
@falk
es ging sich mir eigentlich nur um den code-schnipsel aus lothars
rs232-komponente :
> process (RESET, CLK)> begin> if (RESET='1') then> RX_SR <= "0000";> elsif (rising_edge(CLK)) then> RX_SR <= RX_SR(2 downto 0) & RX;> if (RX_SR = "1000") then RX_Entprellt <= '0'; end if;> if (RX_SR = "0111") then RX_Entprellt <= '1'; end if;> end if;> end process;
diesen teil wollte ich verwenden, da damit mein uart funktioniert (wie
gesagt ich habe auch noch nicht ganz verstanden warum es damit
funktioniert, aber das werde ich schon noch rauskriegen)
ich weiß das das nichts wildes ist oder etwaige patentrechtsverletzungen
damit verbunden wären, möchte aber anstandshalber nachfragen ob es ok
wäre, damit ich in der code-sammlung auchmal einen beitrag in form eines
eigen geschriebenen uarts leisten kann. :-)
daher die frage (die an lothar gerichtet war :-)
>Das Entprellen ist prinzipiell nicht falsch,>wird aber meist nicht gebraucht. Die Abtastung mit baud_clk_16 wirkt>schon entprellend genug. Ausserdem verwenden alle professionellen UARTs>auch nur die mit baud_clk_16 abgetasteten Werte (und bilden die>Mehrheitseintscheidung aus Abtastwert 7,8 und 9. Wenn du dann noch das>Startbit sowie Stopbit prüfst ist wasserdicht.
mir ist klar das es so funktionieren müsste, tut es aber leider nicht.
und einen grund für das nicht-funktionieren habe ich leider auch keinen.
werde das nochmal genauer autesten müssen damit ich nicht dumm sterbe
:-)
dank auch an dich falk für deine kompetenten beiträge und deine prompte
hilfe
gruß
rene
@TheMason: Nimm den Code ruhig, wenn da was geschützt wäre, hätte ich es
nicht veröffentlicht.
Was ich damit mache, ist ein gelatchtes Signal (RX_Entprellt), das den
Zustand nur ändert, wenn eine richtige Flanke am Eingang anliegt.
Ehrlich gesagt ist mir auch nicht ganz klar, wieso das jetzt
funktioniert. Evtl. liegt es an der zusätzlichen Latency von 2
Taktzyklen.
Mit dem synchronisieren meinte ich eigentlich deinen baud_clk_16_z1, der
war aus dem Code nicht als taktsynchron zu erkennen. Erst durch deine
Bemerkung mit dem Ableiten aus dem clk wird das klar:
> außerdem ist baud_clk_16 auch synchron zum clk (da dieser von clk abgeleitet
wird).
Ich mag solche Unklarheiten nicht. Deshalb habe ich mir deinen Code
nochmal angeschaut (für was gibts Mittagspausen?).
Prinzipiell tut sich dein UART etwas schwer mit dem Start:
1
count<=count+'1';
2
3
ifstate=X"0"then
4
ifrx_in_z3='0'then-- solange Startbit aktiv (= 0)!!!
5
ifcount=X"F"then-- was passiert, wenn das Startbit mal
6
state<=X"1";-- nur 15 Takte lang aktiv ist??
7
endif;-- -> das Startbit wird ignoriert
8
else-- und auf einem nachfolgenden Bit gestartet
9
count<=(others=>'0');
10
endif;
Hier würde ich sofort nach dem Erkennen eines Startbits (fallende
Flanke) einen zusätzlichen Zustand einlegen, der auf das erste Bit
wartet.
>Es hängt allerdings auch vom Zeichen selbst ab. Wenn ich z.b. ein 'h'>sende (binär : 01101000) empfängt er es immer richtig. Ein 'a' hingegen>(binär : 01100001) wird fast immer falsch empfangen.
Und jetzt wirds klar: bei einem 'a' kommt nach dem Startbit='0' gleich
eine '1' (LSB zuerst) --> Fehlstart (oft, meistens?).
Ich denke, dass z.B. ein c, e, g, i... auch öfter mal falsch kommt.
Mit einem b, d, f, h, j... gehts dagegen.
Eine kleine Änderung in deinem Code könnte da helfen:
1
count<=count+'1';
2
3
ifstate=X"0"then
4
ifrx_in_z3='0'then
5
ifcount=X"8"then-- halbe Bitzeit vom Startbit abgewartet
@lothar
erstmal danke für das snippet.
muß wohl eben anmerken das der code den ich zu anfang gepostet habe
schon nicht mehr aktuell ist.
>if rx_in_z3 = '0' then> if count = X"F" then> state <= X"1";> end if;>else
war nur der erste schuß. ich habe recht schnell gemerkt das das nicht
funktionieren kann.
daher habe ich in meinem aktuell verwendeten code (habe diesen gerade
nur zuhause rumfliegen) in etwa das verwendet :
if rx_in_z3 = '0' and rx_in_z2 = '1' then
count <= X"0";
state <= X"1";
end if;
um die fallende flanke zu erkennen. daraufhin verzweige ich in zustand 1
der (und das ist auch wieder neuer code) bis zur bit-mitte wartet (also
count = X"8") und dann das start-bit erneut prüft. ist dieses bit = 0,
so gehe ich in zustand 2 der mir meine bits reinschiebt (auch immer nur
in der bit-mitte).
habe ich stattdessen ein start-bit von 1 gehe ich zurück nach zustand 0.
und genau dieser code (ich poste ihn heute abend, damit wir nicht über
alten code reden) funktioniert NUR dann wenn ich deine entprellung
nehme. bei der methode die ich verwendet habe (4-flip-flops die per
baud_clk_16 getaktet werden) empfange ich nichts.
irgendwie finde ich das merkwürdig, zumal (wie falk ja schon sagte) ich
das signal ja auch mit baud_clk_16 einsynchen kann ...
gruß
rene
@TheMason: mit welchem Takt synchronisierst du das
RX_IN-Schieberegister? Mit baud_clk_16 oder (dem wesetlich schnelleren)
clk?
Nur auf ein einziges FF zu verwenden wie in
1
ifrx_in_z3='0'andrx_in_z2='1'then-- steigende Flanke, rx_in_z3 ist älter
bringt dir sicher auch interessante Effekte.
Das wird jetzt ein statistisches Problem: wenn du nicht so richtig
schöne störfreie Signale hast, kann dir ein kurzer 1- oder 0-Spike in
rx_in_z1 eingelesen werden und voila: zwei Takte später erkennst du eine
Flanke und legst schon mal los.
Ich umgehe sowas über das 4-Bit Schieberegister.
Dein Code müsste ohne Schieberegister demnach etwa so aussehen (als
3-Bit-SR):
Am Rande:
Ruhepegel ist normalerweise '1' auf der RX-Strippe.
Ich denke dein Code zuhause wartet auf die fallende Flanke des
Startbits.
Im Text hast du es jedenfalls schon richtig beschrieben.
@lothar
ich habe es mit beiden takten ausprobiert, klappte in keinem der fälle
:-(
aber was du meintest mit den spikes und dem 4-bit-sr ist denke ich mein
problem. ich habe mal bei einer spi-komponente die nicht laufen wollte
meine bedingung (steigende oder fallende flanke) einfach mal über 3 ff
anstatt über 2 gemacht und dann lief es auch. ich vermute das ich hier
ein ähnliches problem habe bzw. das das das selbe problem ist.
@Lothar
>Nur auf ein einziges FF zu verwenden wie inif rx_in_z3 = '0' and rx_in_z2 >= '1'
then -- steigende Flanke, rx_in_z3 ist älter
>bringt dir sicher auch interessante Effekte.>Das wird jetzt ein statistisches Problem: wenn du nicht so richtig>schöne störfreie Signale hast, kann dir ein kurzer 1- oder 0-Spike in>rx_in_z1 eingelesen werden und voila: zwei Takte später erkennst du eine>Flanke und legst schon mal los.
Und bricht bei der Prüfung des Startbits wieder ab.
MFG
Falk
so !
nun hab ich das problem gefunden. durch eine schusseligkeit hab doch
tatsächlich statt
if rx_in_z3 = '1' and rx_in_z2 = '0' then
if rx_in_z3 = '0' and rx_in_z2 = '1' then
zur erkennung der fallenden flanke verwendet ... :-(
konnte ja nicht funktionieren. kaum macht mans richtig. jetzt klappts
auch wenn ich auf baud_clk_16 einsynche.
gruß
rene
Ein paar kleine Anmerkungen.
> process (clk,res, baud_clk_16_z1, baud_clk_16, state, count, rx_in_z3, >rx_in_z2, rx_in_z1, rx_in, rx_buffer)
In einenm getakteten Prozess braucht man nur den Takt und das asynchrone
Reset in der Sensitivity List.
> baud_clk_16_z1 <= baud_clk_16;> if baud_clk_16_z1 = '0' and baud_clk_16 = '1' then
Ist prinzipiell richtig, besser ist es aber, gleich im Baudgenerator ein
entsprechendes Signal zu generieren (Clock enable, einen Takt breit).
Macht das Ganze übersichtlicher.
> if state = X"0" then> if rx_in_z3 = '0' and rx_in_z2 = '1' then> count <= (others => '0');> state <= X"1";> else> count <= (others => '0');> end if;
In beiden Zweigen ist die Zuweisung an count identisch. Also rausnehmen,
der Übersicht wegen.
> if trg_out = '1' then> trg_out <= '0';> end if;
Das soll sicher der Trigger für die nachfolgende Schaltung sein, dass
ein neues Datenbyte angekommen ist. Das macht man sinnvollerwise anders.
Man setzt ein Flag (Data available) und wartet auf das Handschake der
Zielschaltung (Acknowledge). Dann löscht man das Flag wieder. Wenn das
Flag nicht bis zum nächsten empfangenen Zeichen gelöscht wurde (State
"a"), dann gibts nen Buffer Overflow.
Ausserdem solltest du für die States Namen verwenden, keine Zahlen.
Dadurch hat der Compiler die Chance, die für die Zielarchitektur
günstige Kodierung zu wählen (Binär, One-Hot, Gray etc.) Ausserdem wird
es wieder lesbarer.
type state_type is (s_wait, s_start, s_0, s_1, s_2, s_3, s_4, s_5, s_6,
s_7, s_stop);
signal state: state_type;
Aber alles in allem, gute Leistung, hast dich durchgekämpft.
MfG
Falk
@falk
danke für die hinweise. habe diesen code nur mal schnell rausgekramt.
da waren dann noch so sachen drin, die den code nicht gerade "schön"
machen, aber das ist alles in arbeit.
danke auch für das lob :-) aber eine gute leistung ist das nicht wenn
man lediglich aus schusseligkeit auf die falsche flanke reagiert und da
tagelang dran rumsucht ... :-(( vor allem weil ein uart ja echt simpel
ist, und wenn man sich dann selbst so ein blödes "stop-bit" einbaut und
es dann nicht findet ....
da frage ich mich dann schon wie ich es geschafft habe einen kleinen
audio-prozessor zu schreiben (der dann auch noch funktioniert :-)) wenn
ich bei solch blöden sachen schon ins trudeln komme ...
dank und gruß
rene
@TheMason: Freut mich, dass es doch noch geklappt hat, so kurz vorm
Einschlafen. Dann war das doch kein Versehen mit der Flanke, ich dachte
da schon an einen Tippfehler (wie weiter oben schon am Rande vermerkt).
Aber keine Sorge, den Fehler machst du nicht nochmal. Die
Flankengeschichte ist jetzt im Gehirn abgespeichert.
Gruß
Lothar
@lothar
es war doch ein tipp-fehler (statt rx_in_z3 = '1' and rx_in_z2 = '0'
habe ich rx_in_z3 = '0' and rx_in_z2 = '1' verwendet).
also schusseligkeit. das mit der flanken-erkennung hatte ich ja schon
verstanden, nur halt eben einen dämlichen flüchtigkeitsfehler gemacht,
der mich dann tage und nerven gekostet hat.
an dich auch noch einen recht herzlichen dank für deine komponente.
das einsynchen so zu machen find ich recht elegant (schön einfach).
werde mir das mal merken. einfach und effektiv.
gruß
rene