Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> VHDL

So initialisieren Sie RAM aus einer Datei mit TEXTIO

Eine bequeme Möglichkeit, den Block-RAM mit Anfangswerten zu füllen, besteht darin, binäre oder hexadezimale Literale aus einer ASCII-Datei zu lesen. Dies ist auch eine gute Möglichkeit, ein ROM (Read-Only Memory) in VHDL zu erstellen. Schließlich sind RAM und ROM in FPGAs dasselbe, ROM ist ein RAM, aus dem Sie nur lesen.

Die Beispiele in diesem Artikel gehen davon aus, dass die folgenden Konstanten und RAM-Typen am Anfang des deklarativen Bereichs der VHDL-Datei deklariert wurden.

constant ram_depth : natural := 256;
constant ram_width : natural := 32;

type ram_type is array (0 to ram_depth - 1)
  of std_logic_vector(ram_width - 1 downto 0);

Dieser Blogbeitrag ist Teil einer Reihe über die Verwendung der TEXTIO-Bibliothek in VHDL. Lesen Sie die anderen Artikel hier:

Stimulus-Datei in Testbench mit TEXTIO eingelesen

BMP-Datei-Bitmap-Bild, gelesen mit TEXTIO

READLINE, LINE, HREAD, OREAD und BREAD

Die Unterprogramme und Typen, die zum Lesen und Schreiben externer Dateien in VHDL benötigt werden, befinden sich im TEXTIO Paket. Dieses Paket ist Teil von std Bibliothek. Die Standardbibliothek wird immer geladen; Daher müssen wir es nicht explizit mit library importieren Schlüsselwort.

Wir können einfach weitermachen und den TEXTIO verwenden package im Header unserer VHDL-Datei wie folgt:

use std.textio.all;

Wir speichern die RAM-Daten in einer ASCII-Datei, wobei eine Textzeile einem Speicherplatz entspricht. Um eine Textzeile zu lesen, verwenden wir den READLINE Prozedur aus dem TEXTIO Paket. Die Prozedur nimmt zwei Argumente, den Dateinamen als konstanten Input und die geparste Textzeile als inout Variable. Die Prototypdeklaration von READLINE Prozedur und die LINE Der aus der VHDL-Standardspezifikation übernommene Typ ist unten dargestellt.

procedure READLINE (file F: TEXT; L: inout LINE);

type LINE is access STRING; -- A LINE is a pointer
                            -- to a STRING value.

Obwohl die Klasse des LINE -Parameter ist in der Prototyp-Deklaration von READLINE nicht explizit angegeben , es ist eine Variable, da dies die Standardklasse für inout ist Parameter. Die LINE type ist einfach ein Zugriffstyp auf einen String, ein Zeiger auf ein dynamisch zugewiesenes String-Objekt.

VHDL-2008 definiert den OREAD , HREAD , und BREAD Prozeduren zum Extrahieren von Oktal-, Hexadezimal- und Binärwerten aus einem LINE Objekt. Die Methoden zum Lesen von Oktal- und Hexadezimalwerten sind ziemlich ähnlich, die Oktalwerte sind lediglich eine Teilmenge der Hexadezimalwerte. Der Einfachheit halber werden wir in diesem Artikel oktale Lesevorgänge überspringen und uns darauf konzentrieren, wie hexadezimale und binäre Werte aus einer Textdatei gelesen werden.

Der folgende Code zeigt die Definitionen der für uns relevanten Prozeduren, sie sind nur in VHDL-2008 und neueren Revisionen verfügbar. Die OREAD und HREAD Prozeduren gibt es in zwei überladenen Varianten für jeden der unterstützten Ausgabetypen. Der optionale GOOD Die Ausgabe kann zum Erkennen von Lesefehlern verwendet werden, obwohl die meisten Tools einen Fehler oder eine Warnung erzeugen, unabhängig davon, ob diese Ausgabe verwendet wird oder nicht.

procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR;
                                 GOOD : out BOOLEAN);
procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR);

procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR;
                                  GOOD : out BOOLEAN);
procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR);

alias BREAD is READ [LINE, STD_ULOGIC_VECTOR, BOOLEAN];
alias BREAD is READ [LINE, STD_ULOGIC_VECTOR];
Klicken Sie hier, um die Definitionen der Eingabeprozeduren aus der TEXTIO-Bibliothek
procedure READLINE (file F: TEXT; L: inout LINE);

procedure READ (L: inout LINE; VALUE: out BIT;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out BIT);

procedure READ (L: inout LINE; VALUE: out BIT_VECTOR;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out BIT_VECTOR);

procedure READ (L: inout LINE; VALUE: out BOOLEAN;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out BOOLEAN);

procedure READ (L: inout LINE; VALUE: out CHARACTER;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out CHARACTER);

procedure READ (L: inout LINE; VALUE: out INTEGER;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out INTEGER);

procedure READ (L: inout LINE; VALUE: out REAL;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out REAL);

procedure READ (L: inout LINE; VALUE: out STRING;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out STRING);

procedure READ (L: inout LINE; VALUE: out TIME;
                               GOOD: out BOOLEAN);
procedure READ (L: inout LINE; VALUE: out TIME);

procedure SREAD (L: inout LINE; VALUE: out STRING;
                                STRLEN: out NATURAL);
alias STRING_READ is SREAD [LINE, STRING, NATURAL];

alias BREAD is READ [LINE, BIT_VECTOR, BOOLEAN];
alias BREAD is READ [LINE, BIT_VECTOR];
alias BINARY_READ is READ [LINE, BIT_VECTOR, BOOLEAN];
alias BINARY_READ is READ [LINE, BIT_VECTOR];

procedure OREAD (L: inout LINE; VALUE: out BIT_VECTOR;
                                GOOD: out BOOLEAN);
procedure OREAD (L: inout LINE; VALUE: out BIT_VECTOR);
alias OCTAL_READ is OREAD [LINE, BIT_VECTOR, BOOLEAN];
alias OCTAL_READ is OREAD [LINE, BIT_VECTOR];

procedure HREAD (L: inout LINE; VALUE: out BIT_VECTOR;
                                GOOD: out BOOLEAN);
procedure HREAD (L: inout LINE; VALUE: out BIT_VECTOR);
alias HEX_READ is HREAD [LINE, BIT_VECTOR, BOOLEAN];
alias HEX_READ is HREAD [LINE, BIT_VECTOR];
anzuzeigen Klicken Sie hier, um die Definitionen der Eingabeprozeduren aus der std_logic_1164-Bibliothek
procedure READ (L : inout LINE; VALUE : out STD_ULOGIC; GOOD : out BOOLEAN);
procedure READ (L : inout LINE; VALUE : out STD_ULOGIC);

procedure READ (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN);
procedure READ (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR);

alias BREAD is READ [LINE, STD_ULOGIC_VECTOR, BOOLEAN];
alias BREAD is READ [LINE, STD_ULOGIC_VECTOR];
alias BINARY_READ is READ [LINE, STD_ULOGIC_VECTOR, BOOLEAN];
alias BINARY_READ is READ [LINE, STD_ULOGIC_VECTOR];

procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN);
procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR);
alias OCTAL_READ is OREAD [LINE, STD_ULOGIC_VECTOR, BOOLEAN];
alias OCTAL_READ is OREAD [LINE, STD_ULOGIC_VECTOR];

procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN);
procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR);
alias HEX_READ is HREAD [LINE, STD_ULOGIC_VECTOR, BOOLEAN];
alias HEX_READ is HREAD [LINE, STD_ULOGIC_VECTOR];
anzuzeigen

Hex-Werte aus Datei lesen

Hexadezimal ist ein praktisches Format zum Beschreiben von RAM-Inhalten, da zwei Hexadezimalzeichen direkt in ein Byte mit acht Bits übersetzt werden. Jedes Zeichen beschreibt ein Nibble (halbes Byte) und jede Zeile in der Textdatei beschreibt den Inhalt eines RAM-Slots. Die folgende Auflistung zeigt einen Auszug aus der ram_content_hex.txt Datei. Es wurde mit Beispielwerten gefüllt, die von 1 bis 256 dezimal reichen und als Hex geschrieben werden.

12–255256 00000001 00000002 ... 000000FF 00000100

Um die Daten aus der Textdatei zu laden, verwenden wir eine unreine Funktion, die unter ram_type deklariert ist , aber über der RAM-Signaldeklaration. Der folgende Code zeigt den init_ram_hex Funktion, die die Daten aus der Textdatei liest und als ram_type zurückgibt Objekt.

impure function init_ram_hex return ram_type is
  file text_file : text open read_mode is "ram_content_hex.txt";
  variable text_line : line;
  variable ram_content : ram_type;
begin
  for i in 0 to ram_depth - 1 loop
    readline(text_file, text_line);
    hread(text_line, ram_content(i));
  end loop;

  return ram_content;
end function;

Der readline Prozedur innerhalb der for-Schleife liest jeweils eine Textzeile und weist sie dem text_line zu Variable. Dieses Objekt ist vom Typ line , das ist ein Zugriffstyp auf ein Zeichenfolgenobjekt, ein Zeiger auf eine dynamisch zugewiesene Zeichenfolge. In der nächsten Zeile der hread Die Prozedur liest den String aus line -Objekt und wandelt es in einen std_ulogic_vector um . Dieser Typ kann direkt dem std_logic_vector zugeordnet werden aus denen jede RAM-Zelle aufgebaut ist.

Schließlich deklarieren wir das RAM-Signal, während wir unseren init_ram_hex aufrufen Funktion, um die Anfangswerte dafür bereitzustellen:

signal ram_hex : ram_type := init_ram_hex;

HREAD in VHDL-2002 und VHDL-93

Leider ist die HREAD Prozedur ist nur in VHDL-2008 verfügbar. In allen früheren Versionen von VHDL war der Standard READ Verfahren muss stattdessen verwendet werden. Der READ Die Prozedur ist mit vielen verschiedenen Ausgabetypen überladen, aber es gibt keine Option zum Lesen von Hexadezimalwerten.

Lassen Sie uns einen benutzerdefinierten Algorithmus zum Konvertieren eines hexadezimalen ASCII-Zeichens in ein VHDL std_logic_vector schreiben . Zuerst müssen wir die Zeichen einzeln aus dem text_line lesen Objekt, dann decodieren wir ihre Werte und weisen sie dem richtigen Abschnitt des RAM-Slot-Vektors zu. Der folgende Code zeigt eine äquivalente Implementierung von init_ram_hex Funktion, die auch in älteren VHDL-Versionen funktioniert.

impure function init_ram_hex return ram_type is
  file text_file : text open read_mode is "ram_content_hex.txt";
  variable text_line : line;
  variable ram_content : ram_type;
  variable c : character;
  variable offset : integer;
  variable hex_val : std_logic_vector(3 downto 0);
begin
  for i in 0 to ram_depth - 1 loop
    readline(text_file, text_line);

    offset := 0;

    while offset < ram_content(i)'high loop
      read(text_line, c);

      case c is
        when '0' => hex_val := "0000";
        when '1' => hex_val := "0001";
        when '2' => hex_val := "0010";
        when '3' => hex_val := "0011";
        when '4' => hex_val := "0100";
        when '5' => hex_val := "0101";
        when '6' => hex_val := "0110";
        when '7' => hex_val := "0111";
        when '8' => hex_val := "1000";
        when '9' => hex_val := "1001";
        when 'A' | 'a' => hex_val := "1010";
        when 'B' | 'b' => hex_val := "1011";
        when 'C' | 'c' => hex_val := "1100";
        when 'D' | 'd' => hex_val := "1101";
        when 'E' | 'e' => hex_val := "1110";
        when 'F' | 'f' => hex_val := "1111";

        when others =>
          hex_val := "XXXX";
          assert false report "Found non-hex character '" & c & "'";
      end case;

      ram_content(i)(ram_content(i)'high - offset
        downto ram_content(i)'high - offset - 3) := hex_val;
      offset := offset + 4;

    end loop;
  end loop;

  return ram_content;
end function;

Der Algorithmus geht einfach jede Zeile durch, während er sich jedes Zeichen ansieht, und wandelt es in den richtigen Binärwert um. Wenn ein Zeichen gefunden wird, das nicht im Bereich 0x0-0xF liegt, wird ein Bestätigungsfehler in when others ausgelöst Zweig. Der offset Die Variable steuert die Slice-Position innerhalb jeder Speicherzelle, der der decodierte Wert zugewiesen werden soll.

Sie fragen sich vielleicht, warum wir nicht einen benutzerdefinierten hread erstellen Prozedur, anstatt sie innerhalb von init_ram_hex zu codieren Funktion? Dann müssten wir den init_ram_hex nicht ändern überhaupt funktionieren, würden wir einfach unseren benutzerdefinierten hread verwenden Verfahren anstelle des fehlenden Standardverfahrens.

Das würde in den meisten Simulatoren und einigen Synthesizern wie Lattice iCEcube2 funktionieren, aber es wird nicht in Xilinx Vivado synthetisiert. Die folgende Fehlermeldung gibt deutlich an, wo das Problem liegt.

In Vivado:
[Synth 8-27] Prozedurargument vom Typ ‚line‘ wird nicht unterstützt [init_ram_tb.vhd:15]

Klicken Sie hier, um die alternative Implementierung des HREAD-Verfahrens zu sehen
procedure hread(l: inout line; value: out std_logic_vector) is
  variable c : character;
  variable ok : boolean;
  variable i : integer := 0;
  variable hex_val : std_logic_vector(3 downto 0);
begin
  while i < value'high loop
    read(l, c);
  
    case c is
      when '0' => hex_val := "0000";
      when '1' => hex_val := "0001";
      when '2' => hex_val := "0010";
      when '3' => hex_val := "0011";
      when '4' => hex_val := "0100";
      when '5' => hex_val := "0101";
      when '6' => hex_val := "0110";
      when '7' => hex_val := "0111";
      when '8' => hex_val := "1000";
      when '9' => hex_val := "1001";
      when 'A' | 'a' => hex_val := "1010";
      when 'B' | 'b' => hex_val := "1011";
      when 'C' | 'c' => hex_val := "1100";
      when 'D' | 'd' => hex_val := "1101";
      when 'E' | 'e' => hex_val := "1110";
      when 'F' | 'f' => hex_val := "1111";
  
      when others =>
        hex_val := "XXXX";
        assert false report "Found non-hex character '" & c & "'";
    end case;
  
    value(value'high - i downto value'high - i - 3) := hex_val;
    i := i + 4;
  end loop;
end procedure;

Binärwerte aus Datei lesen

Möglicherweise möchten Sie die RAM-Werte als binäre Literale anstelle von Hex-Zeichen speichern, wenn die RAM-Breite kein Vielfaches von 8 ist. Die folgende Auflistung zeigt den gleichen Inhalt wie zuvor, aber im Binärformat dargestellt, indem nur die Zeichen 0 und 1 .

12–255256 00000000000000000000000000000001 00000000000000000000000000000010 ... 00000000000000000000000011111111 00000000000000000000000100000000

Der unten gezeigte Algorithmus dient zum Lesen von Binärwerten aus einer Datei. Es ähnelt dem Lesen von Hexadezimalzahlen, aber in VHDL-2008 sollten Sie den BREAD verwenden Prozeduraufruf statt HREAD . Es übersetzt ein ASCII-Zeichen in einen einzelnen std_ulogic -Wert, der implizit in std_logic umgewandelt wird .

impure function init_ram_bin return ram_type is
  file text_file : text open read_mode is "ram_content_bin.txt";
  variable text_line : line;
  variable ram_content : ram_type;
begin
  for i in 0 to ram_depth - 1 loop
    readline(text_file, text_line);
    bread(text_line, ram_content(i));
  end loop;

  return ram_content;
end function;

Schließlich initialisieren wir das RAM-Signal, indem wir unsere neue unreine Funktion aufrufen, wie im folgenden Code gezeigt.

signal ram_bin : ram_type := init_ram_bin;

BROT in VHDL-2002 und VHDL-93

Wir können unseren Code einfach auf ältere VHDL-Versionen portieren, indem wir READ aufrufen statt BREAD . Der folgende Auszug aus dem VHDL-Standard zeigt den Prototyp von READ an deren Verwendung wir interessiert sind.

procedure READ (L: inout LINE; VALUE: out BIT);

Die READ Prozedur, die einen std_ulogic ausgibt gab es vor VHDL-2008 nicht, daher müssen wir den bit verwenden Version aus dem TEXTIO Bibliothek. Glücklicherweise kann dieser Typ leicht in std_logic konvertiert werden indem Sie den Standard To_StdLogicVector verwenden Funktion.

Die Implementierung von init_ram_bin unten gezeigt funktioniert sowohl in VHDL-2002 als auch in VHDL-93.

impure function init_ram_bin return ram_type is
  file text_file : text open read_mode is "ram_content_bin.txt";
  variable text_line : line;
  variable ram_content : ram_type;
  variable bv : bit_vector(ram_content(0)'range);
begin
  for i in 0 to ram_depth - 1 loop
    readline(text_file, text_line);
    read(text_line, bv);
    ram_content(i) := To_StdLogicVector(bv);
  end loop;

  return ram_content;
end function;

Rückportierung der IEEE std_logic_1164-Bibliothek

Eine Alternative zum Ändern des Codes für ältere VHDL-Versionen ist die Verwendung des Drittanbieterpakets std_logic_1164_additions. Durch Herunterladen und Hinzufügen dieser Bibliothek zu Ihrem Projekt können Sie die neuen Prozeduren auch in VHDL-2002 und VHDL-93 verwenden. Natürlich werden Sie dann viel mehr importieren und Ihr Code wird immer von diesem Paket abhängen.


VHDL

  1. So schützen Sie Aluminium vor Korrosion
  2. Wie sich Metallelemente von Nichtmetallelementen unterscheiden
  3. So erstellen Sie eine CloudFormation-Vorlage mit AWS
  4. Inwiefern unterscheidet sich Cloud Computing von herkömmlichem Computing?
  5. Wie man Kommentare in der C-Programmierung schreibt
  6. Java BufferedReader:Lesen von Dateien in Java mit Beispiel
  7. Python-Durchschnitt:So finden Sie den DURCHSCHNITT einer Liste in Python
  8. Was ist Mikrometer? | Wie liest man ein Mikrometer ab
  9. So rufen Sie einen Funktionsblock von einem OPC UA-Client mithilfe eines Informationsmodells auf
  10. Wie man CNC-Baupläne liest