Ricerca…


Osservazioni

Prima VHDL 1993, due processi concorrenti potevano comunicare solo con i segnali. Grazie alla semantica di simulazione del linguaggio che aggiorna i segnali solo tra fasi di simulazione, il risultato di una simulazione è deterministico: non dipende dall'ordine scelto dallo scheduler di simulazione per eseguire i processi.

[In effetti, questo non è vero al 100%. I processi possono anche comunicare usando l'input / output dei file. Ma se un designer stava compromettendo il determinismo usando i file, non poteva davvero essere un errore.]

La lingua era quindi al sicuro. Tranne che per lo scopo, era quasi impossibile progettare modelli VHDL non deterministici.

VHDL 1993 ha introdotto variabili condivise e la progettazione di modelli VHDL non deterministici è diventata molto semplice.

VHDL 2000 ha introdotto i tipi protetti e il vincolo che le variabili condivise devono essere di tipo protetto.

In VHDL, i tipi protetti sono ciò che assomiglia di più al concetto di oggetti nei linguaggi Object Oriented (OO). Implementano l'incapsulamento delle strutture dati e dei loro metodi. Garantiscono inoltre l'accesso esclusivo e atomico ai propri membri dei dati. Ciò non impedisce completamente il non-determinismo ma, almeno, aggiunge esclusività e atomicità alle variabili condivise.

I tipi protetti sono molto utili nella progettazione di modelli VHDL di alto livello destinati esclusivamente alla simulazione. Hanno parecchie ottime proprietà delle lingue OO. Usarli spesso rende il codice più leggibile, gestibile e riutilizzabile.

Gli appunti:

  • Alcune catene di strumenti di simulazione, per impostazione predefinita, emettono solo avvisi quando una variabile condivisa non è di un tipo protetto.
  • Alcuni strumenti di sintesi non supportano i tipi protetti.
  • Alcuni strumenti di sintesi hanno un supporto limitato di variabili condivise.
  • Si potrebbe pensare che le variabili condivise non siano utilizzabili per modellare l'hardware e debbano essere riservate per la strumentazione del codice senza effetti collaterali. Ma i modelli VHDL consigliati da diversi fornitori EDA per modellare il piano di memoria di RAM (Random Access Memories) a più porte utilizzano variabili condivise. Quindi, sì, le variabili condivise possono essere sintetizzabili in determinate circostanze.

Un generatore pseudo-casuale

Generatori peudo-casuali sono spesso utili durante la progettazione di ambienti di simulazione. Il seguente pacchetto VHDL mostra come utilizzare i tipi protetti per progettare un generatore pseudo-casuale di boolean , bit e bit_vector . Può essere facilmente esteso per generare anche std_ulogic_vector casuale, signed , unsigned . Estendendolo per generare interi casuali con limiti arbitrari e una distribuzione uniforme è un po 'più complicato ma fattibile.

La dichiarazione del pacchetto

Un tipo protetto ha una dichiarazione in cui vengono dichiarati tutti gli accessor pubblici sottoprogrammi. Per il nostro generatore casuale, renderemo pubblica una procedura di inizializzazione dei semi e tre funzioni impure che restituiscono un valore boolean , bit o bit_vector . Si noti che le funzioni non possono essere pure poiché chiamate diverse di una di esse, con gli stessi parametri, possono restituire valori diversi.

-- file rnd_pkg.vhd
package rnd_pkg is
    type rnd_generator is protected
        procedure init(seed: bit_vector);
        impure function get_boolean return boolean;
        impure function get_bit return bit;
        impure function get_bit_vector(size: positive) return bit_vector;
    end protected rnd_generator;
end package rnd_pkg;

Il corpo del pacchetto

Il corpo del tipo protetto definisce le strutture di dati interne (membri) e i corpi dei sottoprogrammi. Il nostro generatore casuale si basa su un registro di spostamento lineare a 128 bit (LFSR) a 128 bit con quattro prese. La variabile di state memorizza lo stato corrente dell'LFSR. Una procedura di throw privato sposta l'LFSR ogni volta che viene utilizzato il generatore.

-- file rnd_pkg.vhd
package body rnd_pkg is
    type rnd_generator is protected body
        constant len: positive := 128;
        constant default_seed: bit_vector(1 to len) := X"8bf052e898d987c7c31fc71c1fc063bc";
        type tap_array is array(natural range <>) of positive range 1 to len;
        constant taps: tap_array(0 to 3) := (128, 126, 101, 99);

        variable state: bit_vector(1 to len) := default_seed;

        procedure throw(n: positive := 1) is
            variable tmp: bit;
        begin
            for i in 1 to n loop
                tmp := '1';
                for j in taps'range loop
                    tmp := tmp xnor state(taps(j));
                end loop;
                state := tmp & state(1 to len - 1);
            end loop;
        end procedure throw;

        procedure init(seed: bit_vector) is
            constant n:   natural            := seed'length;
            constant tmp: bit_vector(1 to n) := seed;
            constant m:   natural            := minimum(n, len);
        begin
            state         := (others => '0');
            state(1 to m) := tmp(1 to m);
        end procedure init;

        impure function get_boolean return boolean is
            constant res: boolean := state(len) = '1';
        begin
            throw;
            return res;
        end function get_boolean;

        impure function get_bit return bit is
            constant res: bit := state(len);
        begin
            throw;
            return res;
        end function get_bit;

        impure function get_bit_vector(size: positive) return bit_vector is
            variable res: bit_vector(1 to size);
        begin
            if size <= len then
                res := state(len + 1 - size to len);
                throw(size);
            else
                res(1 to len) := state;
                throw(len);
                res(len + 1 to size) := get_bit_vector(size - len);
            end if;
            return res;
        end function get_bit_vector;
    end protected body rnd_generator;
end package body rnd_pkg;

Il generatore casuale può quindi essere utilizzato in uno stile OO come in:

-- file rnd_sim.vhd
use std.env.all;
use std.textio.all;
use work.rnd_pkg.all;

entity rnd_sim is
end entity rnd_sim;

architecture sim of rnd_sim is
    shared variable rnd: rnd_generator;
begin
    process
        variable l: line;
    begin
        rnd.init(X"fe39_3d9f_24bb_5bdc_a7d0_2572_cbff_0117");
        for i in 1 to 10 loop
            write(l, rnd.get_boolean);
            write(l, HT);
            write(l, rnd.get_bit);
            write(l, HT);
            write(l, rnd.get_bit_vector(10));
            writeline(output, l);
        end loop;
        finish;
    end process;
end architecture sim;
$ mkdir gh_work
$ ghdl -a --std=08 --workdir=gh_work rnd_pkg.vhd rnd_sim.vhd
$ ghdl -r --std=08 --workdir=gh_work rnd_sim
TRUE    1    0001000101
FALSE   0    1111111100
TRUE    1    0010110010
TRUE    1    0010010101
FALSE   0    0111110100
FALSE   1    1101110010
TRUE    1    1011010110
TRUE    1    0010010010
TRUE    1    1101100111
TRUE    1    0011100100
simulation finished @0ms


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow