VHDL Bits aus Vektor extrahieren

Oft ist ein Vektor aus mehreren Informationen zusammengesetzt (vgl midi_in).

entity a is
port( midi_in: in std_logic_vector(16 downto 0);
      data:    out std_logic_vector(7 downto 0)
    );

Braucht man nun nur einen Bit-Abschnitt so geschieht dies so:

1. Signal von der gewünschten Bit-Breite definieren

signal s_tone_on:  std_logic;
signal s_note_value: std_logic_vector(7 downto 0);
signal s_velocity: std_logic_vector(7 downto 0);

2. Vektor bitweise zuweisten

s_tone_on <= midi_in(16);
s_note_value <= midi_in(15 downto 8);
s_velocity <= midi_in(7 downto 0);

….

VHDL speicher, buffer, register

Inputdaten müssen teilweise abgelegt werden. Wie wird dies in VHDL umgesetzt?

  • man kann per IP RAM anschliessen und dort per Adresse die Daten hineinschreiben.
  • Das VHDL-eigene Speicherprinzip sind jedoch die Flip- Flops. Alle Daten die gespeichert werden sollen, werden in (Vektoren) von Flip-Flops abgelegtff_speicher
    Signale führen Wert in und aus dem Flip-Flop

    -- input
    signal s_next_value: std_logic_vector(7 downto 0);
    -- gespeichert
    signal s_current_value: std_logic_vector(7downto0);
-- input in ff-signal ablegen
s_next_value <= input;

-- input speichern
input_register: process (all)
begin
.    if (reset = '1') then
.        s_current_value <= (others => '0');
.    elsif (clk'event) and (clk = '1') then
.        -- ausgang ff  erhält  eingang ff
.        s_current_value <= s_next_value; 
.  end if; 
end process;

 

speicher_mit_enable

Normalerweise wird das Inputsignal nicht asynchron an den Eingang des Flip-Flops gelegt. Oft entscheidet ein enable_signal, wann die Daten vor den Eingang gelegt werden sollen. (Dies geschieht mit einem Multiplexer.)

-- input speichern
input_register: process (all)
begin
.    if (reset = '1') then
.        s_current_value <= (others => '0');
.    elsif (clk'event) and (clk = '1') then
.        -- ausgang ff  erhält  eingang ff
.        s_current_value <= s_next_value; 

.        if (s_enable = '1') then
           s_next_value <= input;
        else
           s_nex_value <= s_current_value;
        end if;
.  end if; 
end process;

Simulationsprogramm starten (altera)

Vorbereitungen
– testbench ist in vhdl geschrieben
– compile_<projektname>.do ist in shell  und wird von der Konsole (des Simulationsprogrammes) her aufgerufen

Starten der Testbench
1. Neues Projekt anlegen
.  File/New/Project:
.  Ablegen der Simulation
2. Neue Bibliothek ein
File/New/Bibliothek.
Der Ordner wird Work genannt
3. Starten des Kompilierens und des Projekts per Konsole
4. Signale auswählen
.    Als erstes erscheinen nur die Fenster default und object (das sind die Signale).
.   Im Fenster objects auf ein Signal klicken,
.   CTRL A, alle Siganle auswählen
.   Rechte Maus: „Add waves
Ab jetzt sieht man (nach neuem Kompilieren)
.   die Signale im Wave-Fenster.
5. Speichern der Waves:
.    File/Save Format:   speichern in wave_<projektname>.do

Tipp:
In einem Projekt können mehrere testbenches bestehen. Jede erhält einen Namen: tb_midi_interface, tb_top_level, tb_uart.
Zu jeder Testbench gehört ihre eigene Kompilierungsdatei: compile_midi_interface.do, compile_top_level.do, compile_uart.do
und zu jeder Testbench gehören ihre eigene signale: wave_midi_interface.do, wave_top_level.do, wave_uart.do.

..

VHDL signal operationen

Operatoren Beschreibung

not
kann für boolean und signale gebraucht werden

std_logic_signal_1 <= not std_logic_signal_2;

if (not enable_data_1) then
    //

 

and, & , +

std_logic_signal <= signal_a and signal_b;

 

natural, unsigned, integer
Typkonversionen zwischen den logischen Signalen und den natürlichen.

 

Bit Operationen
Signale zusammensetzen
Bist aus Vektor extrahieren
Signal Bits setzen (LSB, MSB)

Testbench compile.do ausführen

Die Testbench wird am einfachsten über die Konsole über das Script gestartet. (Mit tab selbständig vervollständigen)

QuestaSim>   do <pfad>./compile_<projekt>.do
QuestaSim>   do ../simulation/script/compile_<projekt>.do


Script

Das Script compile.do , gibt den Befehl zum Einbinden der Bibliothek, zum Kompilieren der Dateien und startet schlussendlich die testbench.
Das Script liegt im Ordner Work . Alle Pfade bezieht sich vom Ort des Scripts aus.

# create work library
vlib work

# compile files
vcom -2008 -explicit -work work ../../source/counter.vhdl
vcom -2008 -explicit -work work <pfad>/componente_1.vhd
vcom -2008 -explicit -work work <pfad>/top_level.vhd

# compile testbench
vcom -2008 -explicit -work work <pfad>/testbench_top_level.vhd

# run the simulation
vsim -t 1ns -lib work work.testbench_entity

do ../scripts/wave_projekt.do

run 3000.0 ns

Die Reihenfolge der zu kompilierenden Dateien spielt eine Rolle:
– Zuerst die Bibliotheken und Packete
– Dann VHDL: tiefstes Level (spezfischste Komponente)
– Dann VHDL: höheres Level (Zusammenfügender Block)
– Am Schluss: Top-Level
– Testbench

Beim Kompilieren spielt die Version eine Rolle. Gebraucht man process(all), so wird mit der Version 2008 kompiliert. Für die std_textio-Bibliothek braucht man oft 2002, damit man keine Probleme der Doppeldefinition erhält.

Testbench Aufbau

In VHDL gehört die Simulation (schreiben einer Testbench) fix zur Entwicklung dazu. Die Testbench ist in VHDL geschrieben und liegt im Ordner vhdl.
Achtung: Nie in Quartus-Projekt einbinden.  Quartus kann waits und asserts nicht kompilieren.
Ausgeführt (kompiliert) wird die Testbench über die Konsole vom Simlationsprogramm (per Script):

Simulationsprogramm
Bei altera ist questasim die Vollversion des Simulationsprogramms.

work
Einbinden der Bibliothek

Ablage
Im Projektordner wird ein Ordner simulation angelegt. In diesen Ordner kommt – über das Einbinden der Bibliothek  work – der Unterordner Work.
In diesen legt man das Script zum Starten der Simulation compile.do .


Testbench Datei

– die entity hat keine Ports. Sie besteht nur aus Signalen
– die IOs jeder Componente werden an Signale gehängt
– die Testsignale (stimuli) werden in processen gesetzt
– Signale dürfen nur Signalen zugewiesen werden
– die Tests (Vergleichsergebnis von Signalen)
.  finden in den Prozessen statt. Die Processe haben keine Klammern

stimuli:   sind selbst entworfene Signalverläufe
waits:     bestimmen die zeitliche Abfolge unter den
.                  Signalen
reports: geben die Resultat aus
.                  (Konsole oder in File (textbasiertes Testing).
clk:         der clk muss simuliert (erzeugt) werden.
.                 Dies geschieht durch einen eigenen clk-prozess

clk_generator  : process
begin
.   wait for 1 * clk_halfp;
.       clock_sig <= '1';
.   wait for 1 * clk_halfp;
.       clock_sig <= '0';
end process;
entity ram_tb is
end ram_tb;


architecture struct of ram_tb is

---------------------------------------------------------------
-- component declaration
---------------------------------------------------------------
component ram
port(
.    address     : IN STD_LOGIC_VECTOR (15 DOWNTO 0);
.    clock       : IN STD_LOGIC  := '1';
.    data        : IN STD_LOGIC_VECTOR (7 DOWNTO 0);
.    wren        : IN STD_LOGIC ;
.    q           : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)
);
end component;


---------------------------------------------------------------
-- signal instantiation
---------------------------------------------------------------

signal address_sig : STD_LOGIC_VECTOR (15 DOWNTO 0);
signal clock_sig   : STD_LOGIC  := '1';
signal data_sig    : STD_LOGIC_VECTOR (7 DOWNTO 0);
signal wren_sig    : STD_LOGIC ;
signal q_sig       : STD_LOGIC_VECTOR (7 DOWNTO 0);

signal clk_halfp   : time := 10 ns;
signal clk_period  : time := 20 ns;

-- eventuell constanten mit resultaten definieren

begin
---------------------------------------------------------------
-- component instantiation
---------------------------------------------------------------

inst_1 : ram
port map (
.   address  => address_sig,
.   clock    => clock_sig,
.   data     => data_sig,
.   wren     => wren_sig,
.   q        => q_sig
);

---------------------------------------------------------------
-- Set stimuli in process with timing
---------------------------------------------------------------
test_1: process(all)
.    -- Initialwerte setzen

.    -- Signale an Komponenten legen (Stimulation)

.   -- Warten

.   -- Signal von Ausgang Komponente abfangen

.   -- Vergleichen des Outputs mit Erwartung

.   -- Ausgabe Testresulat

end; -- test_1

end; -- architektur

Wie man die Tests startet, wird unter dem Eintrag Testbench ausführen erklärt. Eine professioneller Form des Testens basiert auf textbasierten Scripts.

Hier wird die Syntax der Testbench-Fehler (assert, report, severity,..) beschrieben.

VHDL: textbasierte Test: Aufbau File

File-Variablen definieren
Sie werden in dem Process definiert.

use std.textio.all;
use work.std_logic_textio.all;  // Kompilieren mit 2002

// other code bevor....

--------------------------------------------------------------------
-- testbased test 
--------------------------------------------------------------------
execute_file: process

    -------------------------------------
    -- File handling
    -------------------------------------
    file input_file: TEXT;       
    file output_file: TEXT;    
    
    variable line_in,line_out: Line; 
    variable good: boolean;   
    
    -- token structure: 
    -- type | note_1 | velc_1 | note_2 | velc_2 | numb_note | other
    VARIABLE token_type_n: string(1 to 5);    
    VARIABLE token_note_1: std_logic_vector(7 downto 0) :=(OTHERS => '0');
    VARIABLE token_velocity_1: std_logic_vector(7 downto 0) :=(OTHERS => '0');
    VARIABLE token_note_2: std_logic_vector(7 downto 0) :=(OTHERS => '0');
    VARIABLE token_nmbr_note_out: std_logic_vector(3 downto 0) :=(OTHERS=> '0');
    VARIABLE token_others: std_logic_vector(3 downto 0) :=(OTHERS=> '0');
    

Wie ein Inputfile aufgebaut werden soll, steht im Beitrag zum Inputfile.

 

File öffnen und Ende erkennen
– In VHDL werden Files zeilenweise eingelesen (siehe std_logic_textio)
– Eingelesen wird in einem Process

execute_file: process

-- zuerst File variablen (siehe oben)

BEGIN
    FILE_OPEN(input_file,"../simulation/script/input_midi.txt", READ_MODE);
    FILE_OPEN(output_file,"../simulation/script/result_midi.txt", WRITE_MODE);

 loop    
        ---------------------------------
        -- Check end of file
        --------------------------------
        if endfile(input_file) then  
                
                -- output console read line numbers      
                write(line_out, string'("Number of read lines from file:"));
                writeline(OUTPUT,line_out);                                  
                write(line_out, line_cnt);            
                writeline(OUTPUT,line_out);      
                assert false
                report "End of test"
                severity FAILURE;
            exit;
        end if;

Unmittelbar danach folgt (ebenfalls im loop)

Tokens einlesen

        -----------------------------------
        -- Read 10 token in (and check for error)
        ------------------------------------
        readline(input_file, line_in);     
        line_cnt <= line_cnt + 1;
        
        -- Skip empty lines
        next when line_in'length = 0;

        read(line_in, token_type_n, good);    
            assert good
            report "Error command (type) failed."
            severity ERROR;   
         
        hread(line_in, token_note_1, good);  
            assert good
            report "Error token (note_1) failed."
            severity ERROR;

Zuerst wird zeilenweise eingelsen (und die Zeilen gezählt.)
Dann teilt man die Wörter in Tokens ein. Je nach Tokentyp wird eine andere read-Funktion genommen.

Test durchführen
– Das Vergleichen der Werte einen getakteten Process
– Signale werden ausgelesen und mit Variablenwerten verglichen

test: process(all)
begin

 loop
    if rising_edge(clk) then
        -- Abarbeiten 1. Test ----------------------------------
        if (testtyp_n = 1) then

          -- Signale einlesen
          sig_wert1   <= puls_1;
          sig_wert2   <= data_slc;
          sig_result1 <= adder_out;

          wait for 2 * period;

          -- Test Wert
          if ( sig_wert1 /= wert_n) then

             -- Falls Fehler: Ausgabe in Zeile
             hwrite(line_out, wert_n); 
             write(line_out, string'(" nicht korrekt") ); 

          -- Test Ergebnis     
          elsif ( sig_result1 /= ergebnis_n) then

             -- Falls Fehler: Ausgabe in Zeile
             hwrite(line_out, ergebnis_n);
             write(line_out, string'("Ergebnis falsch"));

          -- Test fehlerfrei
          else
             zeilen_nr <= zeilen_nr + 1;
             hwrite(line_out, testtyp_n)
             write(line_out, string'(" fehlerfrei")); 
          end if;

       -- Abarbeiten 2. Test -----------------------------------
       elsif (testtyp_n = 2) then

            -- Signalwerte auslesen 
            
            -- Vergleich mit Variablen

            -- Ergebnis in Zeile schreiben

      -- Ende Abarbeiten Tests
      else
            assert false
            report "Unbekannter Testtyp"
            severity FAILURE;

     end if;    

     -- Zeile in File schreiben
     writeline(out_file, line_out);

 end loop; 

wait; 
end process;

Good Practice
Arbeitet man mit textbasiertem Debugging, so lässt man das Konsolendebugging (mit ASSERTS) weitmöglichst weg.

Metastabilität bei Flip-Flop

Wechselt das Inputsignal eines Flip-Flops zur falschen Zeit, ist das Ausgangssignal unsicher. Im besten Fall nimmt der Ausgang dann einen selbst gewählten Wert an (Q ode Q), im schlechten Fall „hängt“ sich das Flip-Flop „auf“ und toggelt permanent zwischen Q und Q.

Kritische Zeit beim Eingang
t_setup:   so lange müssen Daten vor dem Clock da sein
.                  (3 bis 12 ns. Cyclone IV: 8 – 10 ns)
t_hold:     so lange müssen Daten nach dem Clock anliegen  (0 bis 1 ns)
.                  Diese Zeit muss grösser sein, als die Durchlaufverzögerung des FF.
t_pd:         Durchlaufverzögerung: Zeit der Daten, bis sie am Ausgang anliegen.
kritscheZeit_FF

 

Treffen neue Daten vor der minimalen Haltezeit ein, so ist unklar, was am Ausgang anliegt.

Ursache für das Nichteinhalten der t_su
Die Clk-Frequenz hängt vom längsten Verzögerungspfad ab. Ist der eingestellte CLK schneller, so  kommt bei mindestens einem FF die Daten zu spät an. Die t_su kann dort nicht eingehalten werden.

Warum ist (nur ein) FF  bereits ein Problem?
Weil das FF im schlimmsten Fall zu Toggeln beginnt. Dadurch erhalten alle nachfolgenden FF toggelnde Signale und die ganze Linie ist unbrauchbar.
Ein metastabiles FF zieht die anderen FF mit sich.

Artikel 1

Timing Durchlaufverzögerung t_pd

Durchlaufverzögerung (propagation delay)
Ein Signal am Eingang erscheint (theortisch) direkt Ausgang. Real jedoch hat jedes Signal eine Gatter- oder Flip-Flop-Verzögerung.

Verzögerung bei Logik
Jedes Gatter braucht eine kurze Zeit, um die Logik auszugeben. Diese nennt man Gatter-Verzögerung bzw. auf englisch progagation delay.
delay_gatter
Nimmt man pro Gatter eine Verzögerung von 5 ns, so hat man nach 3 Gattern einen Pfad von 15 ns.

Verzögerung bei Flip-Flop
Auch jedes Flip-Flop hat nach dem Clk noch eine Verzögerung. Das Signal liegt nicht unmittelbar nach dem Clk am Ausgang an.
Durchlaufverzoegerung_FF

Die Verzögerung (t_pd) darf nicht zu lange sein, weil sonst das Signal nicht mehr anliegt. Die Regel ist, dass die Verzögerung küzer sein muss, als die minimale Haltezeit des Signal (t_hold). Bei den Flip-Flops muss zusätzlich noch die Zeit vor dem Clock berücksichtig werden (t_setup). Mehr dazu unter Metastabilität.

 

 

 

VHDL Latch

In Zustand hängen bleiben
Ein Latch ist ein Verharren in einem Zustand (latch = Zuschnappen, Schliessen). Dies geschieht z.B. wenn bei einer switch-case-Struktur nicht alle Fälle abgefragt werden. Trifft ein nicht definierter Fall ein, so verharrt das System in diesem.

Urachen im Code

// kein else nach if
// .................
begin
        if (cnt = 15) then
            pulse <= '1';
        end if;
 end process;
// kein others in case
// ....................
case state is
            when s0 =>   LEDR_0  <= '1';
            when s1 =>   LEDR_1  <= '1'; GPIO_0_0 <= '1';
            when OTHERS =>  LEDG_7 <= '1';
        
    end case;