Erste Schritte mit VHDL und AI

Für Microcontroller und sonstige "echte" Technik-Themen, die sonst nichts mit ft zu tun haben
Everything technical that has nothing to do with ft
Forumsregeln
Bitte beachte die Forumsregeln!
Antworten
atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 03 Feb 2025, 00:19

Einige kennen ja meine Versuche, mit dem magnetostriktiven Wegaufnehmer. Da ich noch einen älteren 5V-CPLD von Intel (MAX7128SLC84-15) herumliegen habe, wollte ich ausprobieren, ob ich damit CPLD/FPGA-Schaltung zur Messung der Impulsdzeiten von bis zu 4 Impulsen erstellen kann.

Da meine Verilog/VHDL-Kenntnisse sehr gering sind (also eher 0) , dachte ich, dass ich es mit Copilot versuchen könnte.
Ich habe also Copilot angewiesen, ein VHDL-Programm zu für mein Problem zu erstellen. Er hat dann tatsächlich auch eines erstellt, das mir plausibel vorkam und syntaktisch korrekt war. In der IDE (Quartus) kamen dann aber Fehler, die ich sukzessive mit Copilot wegbekommen habe. Das Programm habe ich dann auf den CPLD gebrannt und ein entsprechendes Arduino-Programm dazugestrickt.

Und dann kam nach anfänglicher Euphorie die erwartungsgemäße Enttäsuchung. Es hat nicht funktioniert. :? Wäre auch zu schön gewesen. Also musste ich mich selber etwas tiefer in die Materie einarbeiten. Da ich mich mit Programmierung etwas auskenne, dachte ich, dass das mit VHDL kein Problem sein sollte.
Aber das stimmt leider nicht, da es tatsächlich sehr gravierende Unterschiede gibt. Und dann kämpft man gegen die Lmitationen der Hardware: Eine Variable oder ein if-Block mehr und schon kann das ganze nciht mehr synthetisiert werden, was wohl an meinem schwachbrüstigen Device liegt.

Aber immerhin habe ich nach einiger Mühe tatsächlich eine Lösung gefunden, die allerdings mit der Copilot-Version nicht mehr viel gemein hat.
Und ab und zu habe ich Copilot gefragt wie z.B. welcher GND-Pin ist bei dem CPLD in der Nähe von Pin 64.

Und hier also mein erstes VHDL-Programm, das ich hingedengelt habe bis es funktioniert (die Profis wissen sicher, wie man so was besser machen kann, ich bin aber sehr froh, dass es überhaupt geklappt hat):

Code: Alles auswählen

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity TimeMeasurementCounter is
    Port (
        COUNT_CLK : in STD_LOGIC;             -- 16 MHz clock for time measurement
        READ_CLK : in STD_LOGIC;               -- Clock signal for micro controller read out
        READ_RES : in STD_LOGIC;               -- READ_RES='0' + one READ_CLK resets read counter
        READ_INC : in STD_LOGIC;               -- READ_INC='1' + one READ_CLK incements read counter
        TRIG : in STD_LOGIC;                         -- start the measurement
        SIG : in STD_LOGIC;                               -- signals 1-0-Transition to be measured
        A : in STD_LOGIC_VECTOR (1 downto 0);  -- Nibble address 
        D : out STD_LOGIC_VECTOR (3 downto 0)  -- DAta port for nibble
    );
end TimeMeasurementCounter;

architecture Behavioral of TimeMeasurementCounter is
    signal TC : STD_LOGIC_VECTOR (11 downto 0) := (others => '0');        -- Timer Counter
    type reg_array is array (0 to 3) of STD_LOGIC_VECTOR (11 downto 0);
    signal  R : reg_array := (others => (others => '0'));                                   -- Register for measured times
    signal  RC : STD_LOGIC_VECTOR (2 downto 0) := (others => '0');         -- Index for registration process
     signal  RC2 : STD_LOGIC_VECTOR (1 downto 0) := (others => '0');      -- Index for readout process
     signal  sig_state: STD_LOGIC := '0';                                                        -- state is 1 to indicate that a signal was registered
                                                                                                                      -- will go back to 0 when SIG gets back to 1
    constant DEAD_TIME : INTEGER := 250;                                               -- Pulses registered after XXus * 16MHz
begin

         -- Counter and Signal registration process
    process (COUNT_CLK,SIG,sig_state)
            variable counter : unsigned(11 downto 0);

    begin
        if rising_edge(COUNT_CLK) then
                      if  TRIG = '0'  then             -- Initialization and start of measurement
                TC <= (others => '0');
                RC <= (others => '0');
                --count_enable <= '1';
                R(0) <= (others => '0');
                R(1) <= (others => '0');
                R(2) <= (others => '0');
                R(3) <= (others => '0');
                sig_state <='0';
            else                                                                                -- Counting
                 counter := unsigned(TC);
                 counter := counter +1;
                 TC <= std_logic_vector(counter);
             end if;

             if SIG = '0'  and (sig_state='0') and counter > DEAD_TIME  then  -- Signal registration
                if unsigned(RC) < 4 then   -- register up to 4 signals
                                                R(to_integer(unsigned(RC))) <= std_logic_vector(counter);
                    RC <= std_logic_vector(unsigned(RC) + 1);
                end if;
                      sig_state <= '1';     -- prevent multiple registrations whil SIG is 0
                elsif SIG = '1'  then   -- SIG went to 1 => get prepared for a new measurement
                       sig_state <= '0';
                end if;
          end if;
    end process;


         -- Readout process                     
         process (READ_CLK)                    --- is clocked with a READ_CLK
         begin
          if falling_edge(READ_CLK) then
            if READ_RES='0' then                                        --- Reset the Read Counter
                     RC2 <= (others => '0');
            elsif READ_INC ='1' then                            --- Increment Read Counter
                     RC2 <= std_logic_vector(unsigned(RC2) + 1);
            else                               --- Copy adressed Nibble into D
                     D <= R(to_integer(unsigned(RC2)))(4 * to_integer(unsigned(A)) + 3 downto 4 * to_integer(unsigned(A)));
                  end if;
          end if;


         end process;

end Behavioral;

Als Fazit zu den Verscuhen mit AI kann ich sagen:
Es kommen schnelle Ergebnisse, die aber nur ansatzweise brauchbar sind
Man muss viel iterieren bis es nervt
Bei den Iterationen kommt man manchmal zu Schleifen. Hat man zwei Probleme, können nicht immer beide gelöst werden. Die AI hat mir immer wieder Codes generiert, die das alte Problem wieder drin hatten
Zum Nachschlagen ist Copilot sehr gut geeignet, da es sehr schnell Resultate generiert
Um die eigene Einarbeitung in das Thema kommt man hier nicht umhin, aber da kann die AI auch unterstützen.
Vom Mythos: "Ich spezifiziere, die AI kondiert" sind wir noch weit entfernt.

Benutzeravatar
Defiant
Beiträge: 389
Registriert: 31 Okt 2010, 21:42
Wohnort: Narn Homeworld
Kontaktdaten:

Re: Erste Schritte mit VHDL und AI

Beitrag von Defiant » 03 Feb 2025, 09:39

Kannst du gleich eine Zeit Simulation dazupacken? Macht es einfacher den Code zu analysieren finde ich.
"Propaganda does not deceive people; it merely helps them to deceive themselves."
E Hoffer

juh
Beiträge: 996
Registriert: 23 Jan 2012, 13:48

Re: Erste Schritte mit VHDL und AI

Beitrag von juh » 03 Feb 2025, 11:09

Hallo Florian,

das ist ein interessanter Erfahrungsbericht, dem an sich nichts hinzuzufügen ist, nur dass ich wie an anderer Stelle schon erwähnt sehr vorsichtig geworden bin, was die Verallgemeinerung von einzelnen Erfahrungen für "KI" insgesamt angeht.
atzensepp hat geschrieben:
03 Feb 2025, 00:19
Vom Mythos: "Ich spezifiziere, die AI kondiert" sind wir noch weit entfernt.
M.W. werden KI-Modelle bereits umfassend produktiv in der Softwareentwicklung eingesetzt, d.h. diese Aussage stimmt sicher für den getesteten Fall, m.E. aber nicht pauschal, also für alle Arten von Aufgaben und alle Modelle, die ja z.B. bei Coding-Benchmarks unterschiedlich abschneiden: https://research.aimultiple.com/ai-coding-benchmark/

Die Kunst bei der Nutzung ist m.E. ein Gefühl dafür zu bekommen, was welches KI-Modell gut und relativ verlässlich macht und was nicht und, welche Form der Aufgabenformulierung die besten Ergebnisse bring. Meine Hypothese bei deinem Beispiel wäre, dass FPGA-Code (der noch dazu Hardware-spezifisch ist, oder?) im Gegensatz z.b. zu eine Allerweltssprache wie Python schlicht viel seltener in den Trainingsdaten vorhanden ist und daher die Ergebnisse auch erwartbar schlechter sind. (ChatGPT erzählt mir übrigens, dass es verschiedene spezialisierte Modelle für FPGA gibt.)

Das muss im Umkehrschluss nicht heißen, dass Python-Ergebnisse automatisch alle stimmen, aber die Wahrscheinlichkeit ist höher. Ein weiterer Punkt, der m.E. oft vergessen wird, ist dass man auf unterschiedliche Prompts sehr unterschiedliche Ergebnisse bekommen kann (daher ja auch "prompt engineering"), d.h. die Qualität des Ergebnisses ist eigentlich ein Mischprodukt und nicht alleine in der Verantwortung der KI. Bei Menschen ist es ja übrigens nicht anders, bei denen muss ich Arbeitsanweisungen auch oft individuell anpassen, um die besten Ergebniss zu erhalten.

vg
Jan

jona2004
Beiträge: 164
Registriert: 10 Jun 2011, 22:30

Re: Erste Schritte mit VHDL und AI

Beitrag von jona2004 » 03 Feb 2025, 12:31

Hallo Florian,
Sowas mit AI zu machen ist ja schon spannend. Aus Spaß habe ich angefangen den Code in SystemVerilog (SV) umzuschreiben.
Warum SV: Ich kann SV, VHDL nicht, ist weniger geschwätzig, und ist besonders im englischen Sprachraum viel verbreiteter als VHDL, d.h. auch die KI mag damit besser klarkommen

Habe aber besonders an einer Stelle eine Verständnisschwierigkeit.
So wie ich den Code verstehe passiert folgendes:
Während Trig=1 ist läuft ein 12-bit Zähler mit 16MHz clock hoch, Nach einer DEADTIME wird, wenn das Signal SIG=0 ist wird der Zählerstand in einem Ergebnisfeld von bis zu 4 Werten gespeichert.
Solange TRG=1 ist, können die Ergebnisse können ausgelesen werden mittels einer 4 bit breiten Schnittstelle 2 bit Addresse für die nibbles in dem Ergebnisregister und einem INCR signal um zum nächsten Ergebnisregister weiter zu schalten ausgelesen werden.
Das SIG ist mir nicht klar.
Der Kommentar sagt: "signals 1-0-Transition to be measured" Das würde bedeuten es ist ein "Event", das macht aber leichte Schwierigkeiten, das ja die Dauer von "SIG" u.U. länger als eine 16MHz periode dauern kann. Hier wird mit sigstate wohl die steigende Flanke von SIG erzeugt. Ist das so richtig?
M.e. wäre es einfacher gleich die fallende Flanke des signals im FPGA auszuwerten.
Passt das so?
Grüße Joachim

atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Re: Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 03 Feb 2025, 14:07

Hallo Joachim,

du hast das Verhalten exakt beschrieben! Bei der Auswertung der fallenden Flanke des SIG-Signals hat mir der Quartus-Compiler einen Fehler generiert,
als ob nicht gleichzeitig Flanken von CLK und SIG im gleichen Prozess ausgewertet werden können. Daher habe ich mir mit dem sig_state geholfen. Das war aber zugegebenermaßen eine arge Plackerei, da es nicht funktionieren wollte. Erst nachdem ich sig_state in die Prozess-signatur mit aufgenommen habe, ging es.

Evtl. kann man die Messung in einen eigenen Prozess verlagern, der dann die SIG-Flanke auswertet. Allerdings bin ich mit meinem CPLD am Limit. Schon kleine Änderungen führen dazu, dass das Programm nicht mehr kompiliert.

Aber ich sage nicht, dass der Code optimal ist. Das geht bestimmt besser. Wenn Du eine elegantere Lösung hast, würde sie mich interessieren.

Viele Grüße
Florian

PS: Die 12-Bit sind mir eigentlich noch zu wenig. Ich bräuchte 14 Bit. Aber das sprengt meinen CPLD.

atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Re: Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 03 Feb 2025, 14:18

Defiant hat geschrieben:
03 Feb 2025, 09:39
Kannst du gleich eine Zeit Simulation dazupacken? Macht es einfacher den Code zu analysieren finde ich.
Na klar. Anbei eine Testbench. Ich hatte das mit ghdl probiert. Allerdings wird bei mir D nur mit Status "unknown" angezeigt, als ob ghdl die 2D Struktur nicht auflösen kann.

Code: Alles auswählen

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity TimeMeasurementCounter_tb is
end entity;

architecture testbench of TimeMeasurementCounter_tb is
--architecture Behavioral of testbench is
    signal clk500Hz : STD_LOGIC := '0';
    signal trig : STD_LOGIC := '0';
    signal sig : STD_LOGIC := '0';
    signal a : STD_LOGIC_VECTOR(1 downto 0) := (others => '0');
    signal d : STD_LOGIC_VECTOR(3 downto 0); -- := (others => '0');
    signal zero : STD_LOGIC;
    signal read_res : STD_LOGIC := '0';
    signal read_clk : STD_LOGIC := '0';
    signal read_inc : STD_LOGIC := '0';
    type reg_array is array (0 to 3) of STD_LOGIC_VECTOR (11 downto 0);
    signal  R : reg_array := (others => (others => '0'));              -- Register for measured times

    component TimeMeasurementCounter
        Port (
            COUNT_CLK : in STD_LOGIC;
                        READ_CLK : in STD_LOGIC;
                        READ_RES : in STD_LOGIC;
            READ_INC : in STD_LOGIC;
            TRIG : in STD_LOGIC;
            SIG : in STD_LOGIC;
            A : in STD_LOGIC_VECTOR (1 downto 0);
            D : out STD_LOGIC_VECTOR (3 downto 0)
        );
    end component;

begin
    -- Instanziierung der TimeMeasurement-Komponente
    u0: entity work.TimeMeasurementCounter
        port map (
                    READ_CLK  => read_clk,
                        READ_RES => read_res,
            COUNT_CLK => clk500Hz,
                    READ_INC => read_inc,
            TRIG => trig,
            SIG => sig,
            A => a,
            D => d


        );

    -- Taktgenerierung
    clk_process : process
    begin
        while true loop
            clk500Hz <= '0';
            wait for 1 ms;
            clk500Hz <= '1';
            wait for 1 ms;
        end loop;
    end process;

    -- Testvektor
    test_process : process
    begin
        -- Initialisierung
        trig <= '1';
        sig <= '1';
        wait for 1 ms;

-- Trigger setzen
        trig <= '0';
        wait for 1 ms;
        trig <= '1';

-- 1. Signal        
        wait for 2 ms;
        sig <= '0';
        wait for 1 ms;
        sig <= '1';

-- 2. Signal        
        wait for 5 ms;
        sig <= '0';
        wait for 1 ms;
        sig <= '1';

-- 3. Signal        
        wait for 5 ms;
        sig <= '0';
        wait for 1 ms;
        sig <= '1';


-- 4. Signal        
        wait for 5 ms;
        sig <= '0';
        wait for 1 ms;
        sig <= '1';

  -- Auslesen
        wait for 5 ms;
        read_res <= '0'; -- Reset Readout Counter rc2=0
        READ_INC <= '1';
        wait for 5 ms;


        for k in 1 to 4 loop  -- read 4 values
          for n in 0 to 3 loop
            a <= std_logic_vector(to_unsigned(n, a'length));
            wait for 1 ms; -- Zeit für die Signalanpassung
            READ_CLK <= '0';
            wait for 1 ms;
            READ_CLK <= '1';
            wait for 1 ms;
          end loop;
        end loop;


    end process;
end
architecture testbench;


atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Re: Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 03 Feb 2025, 14:28

juh hat geschrieben:
03 Feb 2025, 11:09
Hallo Florian,

das ist ein interessanter Erfahrungsbericht, dem an sich nichts hinzuzufügen ist, nur dass ich wie an anderer Stelle schon erwähnt sehr vorsichtig geworden bin, was die Verallgemeinerung von einzelnen Erfahrungen für "KI" insgesamt angeht.
atzensepp hat geschrieben:
03 Feb 2025, 00:19
Vom Mythos: "Ich spezifiziere, die AI kondiert" sind wir noch weit entfernt.
Hallo Jan,
ja, meine Aussage "sind wir noch weit entfernt" stimmt eigentlich nicht. Was ich damit zum Ausdruck bringen wollte ist, dass man die generierten "Artefakte" trotzdem mit einer gewissen Vorsicht genießen sollte. Also der IT-Entwickler kann das Ergebnis sicher beurteilen, ein unbedarfter Anwender vielleicht eher nicht.

atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Re: Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 03 Feb 2025, 18:00

Habe mal versucht, das Signal mit falling_edge in einem separaten Prozess zu detektieren. Merkwürdigerweise führt das dazu, dass Edges mehrfach registriert werden, so als ob der falling_edge mehrfach getriggert wird (*). Die Initialisierung von R und RC muss ich im zweiten Prozess machen, da es sonst zu "multiple drivers" kommt.

Code: Alles auswählen

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity TimeMeasurementCounter is
    Port (
        COUNT_CLK : in STD_LOGIC;             -- 16 MHz clock for time measurement
        READ_CLK : in STD_LOGIC;               -- Clock signal for micro controller read out
        READ_RES : in STD_LOGIC;               -- READ_RES='0' + one READ_CLK resets read counter
        READ_INC : in STD_LOGIC;               -- READ_INC='1' + one READ_CLK incements read counter
        TRIG : in STD_LOGIC;                   -- start the measurement
        SIG : in STD_LOGIC;                               -- signals 1-0-Transition to be measured
        A : in STD_LOGIC_VECTOR (1 downto 0);  -- Nibble address 
        D : out STD_LOGIC_VECTOR (3 downto 0)  -- DAta port for nibble
    );
end TimeMeasurementCounter;

architecture Behavioral of TimeMeasurementCounter is
    signal TC : STD_LOGIC_VECTOR (11 downto 0) := (others => '0');     -- Timer Counter
    type reg_array is array (0 to 3) of STD_LOGIC_VECTOR (11 downto 0);
    signal  R : reg_array := (others => (others => '0'));              -- Register for measured times
    signal  RC : STD_LOGIC_VECTOR (2 downto 0) := (others => '0');     -- Index for registration process
    signal  RC2 : STD_LOGIC_VECTOR (1 downto 0) := (others => '0');    -- Index for readout process
    signal  sig_state: STD_LOGIC := '0';                               -- state is 1 to indicate that a signal was registered
                                                                            -- will go back to 0 when SIG gets back to 1
    constant DEAD_TIME : INTEGER := 250;                                -- Pulses registered after 40us * 16MHz

     signal TCOLD : STD_LOGIC_VECTOR (11 downto 0) := (others => '0');  -- EDIT: auxilary variable for SIG-Debouncing

begin

         -- Counter and Signal registration process
    process (COUNT_CLK,SIG,sig_state)
            variable counter : unsigned(11 downto 0);

    begin
        if rising_edge(COUNT_CLK) then
            if  TRIG = '0'  then             -- Initialization and start of measurement
                TC <= (others => '0');
            else                                                                                -- Counting
                 counter := unsigned(TC);
                 counter := counter +1;
                 TC <= std_logic_vector(counter);
              end if;
            end if;
         end process;

         process (TRIG,SIG)
            variable counter : unsigned(11 downto 0);
            variable rccounter : unsigned (2 downto 0);
         begin
            counter := unsigned(TC);
            if  TRIG='0' then
                RC <= (others => '0');
                R(0) <= (others => '0');
                R(1) <= (others => '0');
                R(2) <= (others => '0');
                R(3) <= (others => '0');
                TCOLD <= TC;      -- EDIT: for SIG-Debouncing
            elsif falling_edge(SIG) and (unsigned(TC)-unsigned(TCOLD)) > 10 and counter > DEAD_TIME  then  -- Signal registration
                rccounter := unsigned(RC);
                if rccounter < 4 then   -- register up to 4 signals
                   R(to_integer(unsigned(RC))) <= std_logic_vector(unsigned(TC));
                   rccounter := rccounter+1;
                   RC <= std_logic_vector(rccounter);
                   TCOLD <= TC;   -- EDIT: for SIG-Debouncing
                 end if;
            end if;
    end process;


         -- Readout process                     
         process (READ_CLK)                    --- is clocked with a READ_CLK
         begin
          if falling_edge(READ_CLK) then
            if READ_RES='0' then                                        --- Reset the Read Counter
                     RC2 <= (others => '0');
            elsif READ_INC ='1' then                            --- Increment Read Counter
                     RC2 <= std_logic_vector(unsigned(RC2) + 1);
            else                               --- Copy adressed Nibble into D
                     D <= R(to_integer(unsigned(RC2)))(4 * to_integer(unsigned(A)) + 3 downto 4 * to_integer(unsigned(A)));
                  end if;
          end if;
         end process;
end Behavioral;
                        

(*) EDIT: Problem gefunden und behoben: Das SIG-Signal zeigt tatsächlich hochfrequente Schwingungen, die vom CPLD registriert werden. Triggerung nur, wenn das folgende Signal 10 Counts später kommt.

jona2004
Beiträge: 164
Registriert: 10 Jun 2011, 22:30

Re: Erste Schritte mit VHDL und AI

Beitrag von jona2004 » 04 Feb 2025, 15:31

Hallo Florian,
Zu dem edit vom letzten Post - hier sollte man die Signale entprellen. vermutlich sind TRIG, und die INC, RESET inputs harmlos da von einem uC kommend, also bliebe SIG übrig.
Bei Interesse kann ich einen Systemverilog code einstellen, die gewünschte Funktion nach meinem Verständnis nachbildet. (zZt. noch ohne entprellen, kostet natürlich weitere Flipflops)
Grüße Joachim

atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Re: Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 04 Feb 2025, 18:07

jona2004 hat geschrieben:
04 Feb 2025, 15:31
...
Bei Interesse kann ich einen Systemverilog code einstellen, die gewünschte Funktion nach meinem Verständnis nachbildet. (zZt. noch ohne entprellen, kostet natürlich weitere Flipflops)
Hallo Joachim,

ich habe auf jeden Fall großes Interesse und würde mich über eine entsprechende Funktion sehr freuen.

Grüße
Florian

jona2004
Beiträge: 164
Registriert: 10 Jun 2011, 22:30

Re: Erste Schritte mit VHDL und AI

Beitrag von jona2004 » 05 Feb 2025, 17:40

Hallo Florian,
Leider hatte ich die letzten beiden tage recht viel zu tun. Da die files nicht allzu groß sind habe ich die mail hier per code tag reinkopiert. Ich habe den code recht hardware nah geschrieben man kann praktisch fir Flipflops zählen. Für das entprellen habe ich eine evtl. "Luxusvariante".
Zuerst muss der Puls eine breite von 3 clock Pulsen (alles parametrizierbar) haben.
Dann darf der nächste entprellte pulse nicht vor 10 Zyklen kommen sonst wird er auch ignoriert.
Bei Frage oder Änderungswünschen gerne melden.
File 1 hat den synthetisierbaren code.
File 2 ist eine simple testbench zum simulieren ob das so alles klappt.
Grüße Joachim

Code: Alles auswählen

module ftcntr #(
  parameter COUNT_W = 12,       // jmk added to ease readability and parameterization
  parameter NIBBLE_SZ = 4,
  parameter NIBBLES_N = 3,     // number of nibbles
  parameter IDX_NIBBLE_W = 2, // index size to address nibbles
  parameter RESULTS_N = 4,     // number of result registers
  parameter IDX_RES_W = 2    // index size to address results
) (
    input  logic count_clk,  // 16 MHz clock for time measurement
    input  logic read_clk,   // clock signal for micrcontroller read out
    input  logic read_res_b, // synchronous reset active low
    input  logic read_inc,   // increments read counter if '1'
    input  logic trig,       // start the measurement
    input  logic sig,        // input signal
    input  logic [IDX_NIBBLE_W-1:0] adr,  // nibble address
    output logic [NIBBLE_SZ-1:0] data  // data nibble
);
  localparam DEAD_TIME_CNT = 250;
  localparam DEB_TIME_CNT = 3;   // counts signal + 1 must be stable debounce
  localparam DEB_TIME_W = $clog2(DEB_TIME_CNT); // required width
  localparam MSK_TIME_CNT = 10;  // counts during a new edge is iggnored
  localparam MSK_TIME_W = $clog2(MSK_TIME_CNT); // required width

  // local signals
  logic [COUNT_W-1:0] counter;            // timer counter    
  logic [RESULTS_N-1:0][COUNT_W-1:0] results;  // storage of the time captures
  logic [NIBBLES_N-1:0][NIBBLE_SZ-1:0] result;  // packed nibbles for ease of read
  logic [IDX_RES_W-1:0] store_idx;        // address to store counter values
  logic [IDX_RES_W-1:0] read_idx;         // address of result array during read
  
  logic sig_meta;    // to prevent meta stabilities
  logic signal;      // synchronized input signal
  logic tst_sig;     // signal to check against for debouncing
  logic deb_sig;     // debounced signal
  logic deb_sig_dly; // delayed debounced signal to detect edge
  logic valid_edge;  // see code below set to falling
  logic msk_store;   // mask storing a counter value
  logic do_store;    // event to register a counter value
  logic [DEB_TIME_W-1:0] deb_cnt;  // counter used for debouncing
  logic [MSK_TIME_W-1:0] msk_cnt;  // counter used for masking too fast pulses

  // the counter as a backbone
  always_ff @(posedge count_clk) begin
    if (!trig) counter <= 'b0;
    else counter <= counter + 1'b1;
  end

  // Signal processing chain
  // 1 - Synchronizer to prevent meta stability
  always_ff @(posedge count_clk) begin 
    if (!trig) begin // good preactice to initialize
      sig_meta <= 'b0;
      signal   <= 'b0;
    end
    else begin
      sig_meta <= sig;
      signal   <= sig_meta;
    end
  end

  // 2 - debounce the input signal, must be stable for DEBCOUNT
  // consecutive samples
  always_ff @(posedge count_clk) begin
    if (!trig) begin
      deb_cnt <= 'b0;
      tst_sig <= 'b0;
      deb_sig <= 'b0;
    end
    else begin
      if (tst_sig != signal) begin
        deb_cnt <= DEB_TIME_CNT;  // change start over
        tst_sig <= signal;
      end
      else begin
        if (deb_cnt == 'b0) deb_sig <= tst_sig;
        else deb_cnt <= deb_cnt - 1'b1;
      end
    end
  end

  // 3 - detect the active edge coded as falling here
  always_ff @(posedge count_clk) begin
    if (!trig) deb_sig_dly <= 1'b0;
    else deb_sig_dly <= deb_sig;
  end

  assign valid_edge = ~deb_sig & deb_sig_dly;  // falling edge

  // 4 - mask a new valid edge for the next counts
  always_ff @(posedge count_clk) begin
    if (!trig) msk_cnt <= 'b0;
    else begin
      if (valid_edge) begin
        msk_cnt <= MSK_TIME_CNT; // start counting the blockage time
      end
      else begin
        if (msk_cnt != 'b0) msk_cnt <= msk_cnt - 1'b1;
      end
    end
  end
  
  // overall store mask
  assign msk_store = (counter < DEAD_TIME_CNT) || (msk_cnt != 'b0); // block too fast pulses
  assign do_store = valid_edge && !msk_store;

  // storing the result
  always_ff @(posedge count_clk) begin
    if (trig == 1'b0) begin
      store_idx <= 'b0;
      results <= {RESULTS_N{{COUNT_W{1'b0}}}};
    end
    else begin
      if (do_store) begin  // TODO what should happen in case of too many stores?
        results[store_idx] <= counter;
        store_idx <= store_idx + 1'b1;
      end
    end
  end

  // the stuff responsible for read could be put in a separate verilog module?
  always_comb begin
    result = results[read_idx];
    data = result[adr];   // output is not registered saves
  end
  
  // Read process adr picks the nibble within the result selected by
  // the read_idx; read_idx is incremented by a special (command) same
  // as the synchronous read
  always_ff @(negedge read_clk) begin 
    if (!read_res_b) begin
        read_idx <= 'b0;
    end
    else begin
      if (read_inc) begin
        read_idx <= read_idx + 1'b1;
      end
    end
  end
endmodule
Testbench

Code: Alles auswählen

module ftcntr_tb;

  parameter COUNT_W   = 12; // counter width in bits is read in nibbles of 4
  parameter NIBBLE_SZ = 4;  // definition of a "nibble"
  parameter NIBBLES_N = int'($ceil(real'(COUNT_W) / real'(NIBBLE_SZ)));  // number of nibbles in count
  parameter IDX_NIBBLE_W = $clog2(NIBBLES_N);    // index size to address nibbles 
  parameter RESULTS_N = 4; // number of result registerss
  parameter IDX_RES_W  = $clog2(RESULTS_N);

  logic count_clk;  // 16 MHz clock for time measurement
  logic read_clk;   // clock signal for microcontroller read out
  logic read_res_b; // synchronous reset active low
  logic read_inc;   // increments read counter if '1'
  logic trig;       // start the measurement
  logic sig;        // signals a 1-0 Transition to be measured
  logic [1:0] adr;  // nibble address
  logic [NIBBLE_SZ-1:0] data; // data nibble


// Testbench instantiates the synthesizable logic
ftcntr #(
    .COUNT_W(COUNT_W),
    .NIBBLE_SZ(NIBBLE_SZ),
    .NIBBLES_N(NIBBLES_N),
    .IDX_NIBBLE_W(IDX_NIBBLE_W),
    .RESULTS_N(RESULTS_N),
    .IDX_RES_W(IDX_RES_W)
)
top (
  .count_clk(count_clk),
  .read_clk(read_clk),
  .read_res_b(read_res_b),
  .read_inc(read_inc),
  .trig(trig),
  .sig(sig), 
  .adr(adr), 
  .data(data)
);

// Stimulus code starts here
parameter PER = 60;
parameter PER2 = PER/2;
parameter RDPER = 40;
parameter RDPER2 = RDPER/2;

initial begin
  $dumpfile("ftcntr");
  $dumpvars(0, ftcntr_tb);
end

logic [3:0] nibble;

initial begin
  count_clk = 'b0;
  read_clk = 'b1;     // acts on falling edge
  read_res_b = 'b0;   // 
  read_inc = 'b0;   // needs a clock to increment
  trig = 'b0;
  sig  = 'b1;  // measure falling edge
  adr = 2'b0;
end

always begin
  #PER2 count_clk = ~count_clk;  // about 16MHz
end

initial begin
  #(10*PER)  trig = 1'b1;
  #(50*PER)  sig = 1'b0;  // within DEAD TIMEshould be ignored
  #(4*PER)   sig = 1'b1;
  #(500*PER) sig = 1'b0;  // 0 too short
  #(3*PER)   sig = 1'b1;  
  #(20*PER)  sig = 1'b0;  // 0 register this one 
  #(5*PER)   sig = 1'b1; 
  #(5*PER)   sig = 1'b0;  // too fast should not count as #1
  #(8*PER)   sig = 1'b1; 
  #(100*PER) sig = 1'b0;  // 1
  #(8*PER)   sig = 1'b1;
  #(70*PER)  sig = 1'b0;  // 2
  #(8*PER)   sig = 1'b1;
  #(50*PER)  sig = 1'b0;  // 3
  #(8*PER)   sig = 1'b1;
  #(20*PER)  read_all(); // read the result storage

  #(1000*PER) $finish;
end

// read all captured values
task read_all(
);
logic [RESULTS_N-1:0][COUNT_W-1:0] results; // packed result array
logic [COUNT_W-1:0] result;
begin
  #4 read_inc = 1'b0;
  read_clk = 1'b1;      // reset result index
  #8 read_res_b = 1'b0;
  #10 read_clk = 1'b0;
  #(RDPER2) read_clk = 1'b1;
  #2 read_res_b = 1'b1;

  for (int i = 0; i < RESULTS_N; i++) begin
    read_result(.result(result));
    $display("Value %2d: %d", i, result);
    results[i] = result;
    #8 read_inc = 1'b1;
    #10 read_clk = 1'b0;
    #(RDPER2) read_clk = 1'b1;
    #2 read_inc = 1'b0;
  end
end
endtask

// read one captured value
task read_result (
  output logic [COUNT_W-1:0] result
);
begin
  logic [2:0][NIBBLE_SZ-1:0] d; // local storage
  adr = 2'b10;   // TODO parameterize max nibble

  for (int i = 0; i < NIBBLES_N; i++) begin  // nibble loop
    #(RDPER2) read_clk = 1'b0;  // works on falling edge
    d[adr] = data;
    #(RDPER2) read_clk = 1'b1;
    adr = adr - 1'b1;
  end
  result = d;
end
endtask

endmodule

atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Re: Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 05 Feb 2025, 21:35

Hallo Jonas,

vielen Dank für das Programm! Ich bin grad dabei es auf Quartus 13.0.1 zu bringen.
Bisher bekomme ich allerdings diverse Errors: die Quotes ' klappen nicht, int und always_ff sind nicht bekannt, i++ geht nicht, sondern nur i=i+1.
Die härteste Nuss ist aber, dass always_comb nicht klappt.

Mir scheint es so, als ob meine Quartus-Version einen anderen Verilog-Dialekt versteht (VErilog vs. System Verilog?) . Ich probier' weiter.

Viele Grüße
Florian

EDIT: das war's man muss in der IDE auf SystemVerilog umstellen. Dann kompilierts. :)

jona2004
Beiträge: 164
Registriert: 10 Jun 2011, 22:30

Re: Erste Schritte mit VHDL und AI

Beitrag von jona2004 » 05 Feb 2025, 21:53

Hallo Florian,
laut Intel Webpage versteht Quartus Systemverilog 2012 standard.
gegen den ist der code durch LINT gelaufen und auch simuliert.
(verilator als auch ikarus verilog)
always_comb und always_ff sind viel älter als 2012. ich vermute irgendwo gibt es einen switch bei Quartus.
Noch was die filenamen sollten extension .sv haben
Viel Erfolg Joachim

atzensepp
Beiträge: 818
Registriert: 10 Jul 2012, 21:40
Wohnort: Uttenreuth

Re: Erste Schritte mit VHDL und AI

Beitrag von atzensepp » 05 Feb 2025, 22:08

Hallo,

Es funktioniert tatsächlich, sogar mit 13 bit! :D
Das Einzige, was noch stört ist, dass wenn mehr als 4 Signale kommen die vorderen Einträge wieder überschrieben werden.

EDIT: Das habe ich durch ein zusätzliches done-Bit gelöst. Das Programm gefällt mir, weil es sehr gut parametriert ist.
Danke nochmal!

Grüße
Florian

jona2004
Beiträge: 164
Registriert: 10 Jun 2011, 22:30

Re: Erste Schritte mit VHDL und AI

Beitrag von jona2004 » 05 Feb 2025, 23:08

Hallo Florian,
schön. Das Überschreiben hatte ich mit TODO schon markiert wusste aber nicht wie das gewünschte Verhalten sein sollte.
13 bit hatte ich nicht probiert nur 14.
Schönen Abend Joachim

Antworten