VHDL

maskinvarebeskrivende språk

VHDL er et maskinvarebeskrivende språk for FPGA-er og ASIC-er. VHDL kommer fra VHSIC hardware description language, der VHSIC er en forkortelse for very-high-speed integrated circuit.

VHDL
Tilblivelse1983
Siste versjon(er)IEEE 1076-2019
Typetildeling sterk
Påvirket av
Ada

Historie rediger

VHDL ble utviklet for Det amerikanske forsvarsdepartementet for å kunne dokumentere oppførselen til ASIC-er som leverandører brukte i utstyr. VHDL ble altså utviklet som et alternativ til store, komplekse implementasjonsspesifikke manualer.

Ønsket om å kunne simulere denne dokumentasjonen var så åpenbar at logiske simulatorer som kunne lese VHDL-filer ble utviklet. Det neste steget var utviklingen av logiske synteseverktøy som leste VHDL og ga ut en definisjon av kretsens fysiske implementasjon. Moderne synteseverktøy kan kjenne igjen RAM, tellere og aritmetiske blokker i koden, og implementere dem slik brukeren ønsker. Det betyr at den samme VHDL-koden kan bli syntetisert forskjellig ut fra krav om minst mulig areal, lavest effektforbruk, høyeste klokkefrekvens, eller andre krav.

VHDL låner mye av konseptet sitt (for eksempel stykke-notasjonen for å indeksere en del av en en-dimensjonal tabell) og syntaksen sin fra programmeringsspråket Ada. VHDL har begreper for å håndtere parallelliteten knyttet til maskinvaredesign, men disse begrepene (prosesser) er forskjellige når det gjelder syntaks sammenlignet med de parallelle begrepene i Ada (oppgaver). I likhet med Ada er VHDL sterkt typebestemt og skiller ikke mellom store og små bokstaver. VHDL har mange egenskaper man ikke finner i Ada, som for eksempel et utvidet sett av Boolske operatorer som inneholder nand og nor, for å kunne representere operasjoner som er vanlige i maskinvare direkte. VHDL tillater også tabeller å bli indeksert i begge retninger (stigende eller synkende) fordi begge konvensjonene er brukt i maskinvare, mens Ada (som de fleste andre programmeringsspråk) bare tillater stigende indeksering. Grunnen til at de to språkene er såpass like er at Det amerikanske forsvarsdepartementet krevde at så mye som mulig av syntaksen skulle være basert på Ada, slik at en unngikk å finne opp konsepter på nytt som allerede var grundig testet under utviklingen av Ada.

Den første versjonen av VHDL, designet etter IEEE standard 1076-1987, inkluderte en lang rekke datatyper, blant annet numeriske (heltall og reelle tall), logiske (bit og boolske), tegn og tid, tabeller bestående av bit – kalt bit_vector, og tabeller bestående av character – kalt string.

Et uløst problem ved denne utgaven var "multiverdi-logikk", hvor et signals driv-styrke (ingen, svak eller sterk) og ukjente verdier blir tatt hånd om. Dette krevde IEEE standard 1164 som definerte de logiske typene som kan ta 9 forskjellige verdier: den skalare std_ulogic og dens vektorversjon std_ulogic_vector.

Den andre utgaven av IEEE 1076, i 1993, hadde en mer konsistent syntaks, tillot mer fleksible navn, utvidet character-typen til å akseptere ISO-8859-1 trykkbare tegn, xnor-operatoren ble lagt til, etc.

Mindre endringer i standarden (2000 og 2002) la til idéen om beskyttede typer (lignende konseptet om klasser i C++) og fjernet noen restriksjoner fra portsammenkoblingsregler.

I tillegg til IEEE standard 1164, flere understandarder ble introdusert for å utvide funksjonaliteten til språket. IEEE standard 1076.2 forbedret håndtering av reelle og komplekse datatyper. IEEE standard 1076.3 introduserte typene signed og unsigned for å forenkle aritmetiske operasjoner på vektorer. IEEE standard 1076.1 (kjent som VHDL-AMS) er en utvidelse som tilbyr analog og blandet design.

Noen andre standarder støtter en bredere bruk av VHDL, især utvidelsene VITAL (VHDL Initiative Towards ASIC Libraries) og design av mikrobølge-kretser.

I juni 2006 godkjente VHDL Technical Committee of Accellera (delegert av IEEE til å jobbe med den neste oppdateringen av standarden) den såkalte Draft 3.0 av VHDL-2006. I tillegg til å beholde full kompatibilitet med eldre versjoner, tilbyr den foreslåtte standarden flere utvidelser som gjør skrivingen og behandlingen av koden enklere. Viktige forandringer består av å inkludere understandarder (1164, 1076.2, 1076.3) i den genereller 1076-standarden, et utvidet sett med operatorer, mer fleksibel syntaks når det gjelder «case»- og «generate»-uttrykk, inkludering av VHPI (grensesnitt til C/C++-språk), og en del av PSL (Property Specification Language). Disse forandringene burde forbedre kvaliteten på syntetiserbar VHDL-kode, gjøre testbenker mer fleksible, og tillate en mer utstrakt bruk av VHDL for systemnivåbeskrivelser.

I februar 2008 godkjente Accellera VHDL 4.0, uformelt kjent som VHDL 2008, som tar tak i mer enn 90 punkter som ble oppdaget i prøveperioden for versjon 3.0, og inkluderer forbedrede generiske typer. I 2008 utgav Accellera VHDL 4.0 til IEEE for avstemning angående inkludering i IEEE 1076-2008. VHDL-standarden IEEE 1076-2008 ble godkjent av REVCOM i september 2008.

Design rediger

VHDL er et relativt universelt språk, og det krever ikke en simulator å kjøre koden på. Det er mange VHDL-kompilatorer som lager eksekverbare binærfiler. Det kan lese og skrive filer på vertsdatamaskinen, så en kan skrive et VHDL-program som genererer et annet VHDL-program som tas med i det ferdige designet. På grunn av denne universelle oppførselen er det mulig å skrive en testbenk i VHDL som verifiserer funksjonaliteten til designet ved å bruke filer på vertsdatamaskinen til å definere stimuli, samhandler med brukeren, og sammenligner resultatet med det som er forventet.

Det er relativt enkelt for en uerfaren utvikler å skrive kode som simuleres korrekt, men som ikke kan realiseres i ekte maskinvare, eller som er for stor til å kunne realiseres i praksis. En spesiell fallgruve er uønsket generering av transparente vipper (latches) i stedet for D-vipper som lagringselement.

Man kan designe maskinvare i en VHDL IDE (som Xilinx eller Quartus) for å generere RTL-skjema for det gjeldende designet. Etterpå kan det genererte skjemaet bli verifisert ved hjelp av simuleringsprogramvare (som ModelSim) som viser bølgediagram av innganger og utganger til kretsen etter å ha generert en egnet testbenk. For å generere en egnet testbenk for en enkelt krets eller VHDL-kode må inngangene defineres korrekt. For eksempel trenger en klokkeinngang en sløyfeprosess eller et iterativt uttrykk.

Hovedfordelen ved å bruke VHDL til systemdesign er at det tillater at systemet blir beskrevet (modellert) og verifisert (simulert) før synteseverktøyet oversetter designet til virkelig maskinvare (porter og forbindelser).

En annen fordel er at VHDL tillater beskrivelse av et parallelt system (mange deler, hver med sin egen underoppførsel, som jobber sammen på samme tid). VHDL er et dataflyt-språk, i motsetning til prosedyriske datamaskinspråk som BASIC, C og assemblykode som alle kjører sekvensielt, en instruksjon av gangen.

En siste punkt er at når en VHDL-modell blir oversatt til "porter og ledninger" som igjen blir avbildet på en programmerbar logisk enhet, som for eksempel en CPLD eller FPGA, er det den faktiske maskinvaren som blir konfigurert, i stedet for at VHDL-koden blir "kjørt" på en prosessor.

Komme i gang rediger

Selv om erfaring innen datamaskin-programmering (som C eller C++) er hjelpsomt, er det ikke nødvendig. Gratis VHDL-simulatorer er lett tilgjengelige, og selv om disse har begrenset funksjonalitet sammenlignet med kommersielle VHDL-simulatorer, er de mer enn gode nok for bruk på privat basis. Hvis brukeren har som mål å lære RTL-programmering (design av maskinvare i VHDL i motsetning til å kun dokumentere eller simulere maskinvare) trengs det også en syntese/design-pakke.

I likhet med VHDL-simulatorer er også gratis FPGA-synteseverktøy lett tilgjengelige, og er mer enn gode nok for bruk på privat basis. Tilbakemelding fra synteseverktøyet gir brukeren en følelse av forskjellene ved ulike kodestiler. Et visningsprogram viser skjemaet/portene etter syntetisering. Mange FPGA-designpakker tilbyr alternative måter å lese designet på, slik som blokkdiagram (skjema) og tilstandsdiagram. Disse tilbyr en nyttig mal for å kode gitte repeterende strukturer, eller komplekse tilstandsdiagrammer. I tillegg er de vedlagte veiledningene og eksemplene verdifulle hjelpemidler.

Nesten alle FPGA-design og -simuleringsverktøy støtter både VHDL og Verilog, et annet maskinvarebeskrivende språk.

Kodeeksempler rediger

I VHDL består et design av minimum en entity som beskriver grensesnittet og en architecture som inneholder den faktiske implementasjonen. I tillegg importerer de fleste design biblioteksmoduler. Noen design består også av flere arkitekturer og konfigurasjoner.

En enkel OG-port i VHDL:

-- (this is a VHDL comment)

-- import std_logic from the IEEE library
library IEEE;
use IEEE.std_logic_1164.all;

-- this is the entity
entity ANDGATE is
   port ( 
         IN1 : in std_logic;
         IN2 : in std_logic;
         OUT1: out std_logic);
end ANDGATE;

architecture RTL of ANDGATE is
begin

  OUT1 <= IN1 and IN2;

end RTL;

Selv om eksempelet ovenfor kanskje virker vidløftig for HDL-noviser, må man huske på at mange deler er enten valgfrie eller trengs kun å skrives én gang. Vanligvis er enkle funksjoner som dette en del av en større oppførselsmodul, og ikke en separat modul. I tillegg kan bruken av typen std_logic ved første øyekast virke overdrevet. Man kunne uten problemer ha brukt den innebygde typen bit og dermed sluppet å importere biblioteket i begynnelsen. Men ved å bruke denne 9-verdilogikken (U,X,0,1,Z,W,H,L,-) i stedet for enkle bits (0,1) får brukeren et meget kraftig simulerings- og feilsøkingsverktøy som for øyeblikket ikke eksisterer i andre maskinvarebeskrivende språk (HDL).

I eksempelet som følger vil man se at VHDL-kode kan skrives veldig kompakt. Men erfarne designere unngår vanligvis disse kompakte formene og bruker heller mer ordrike kodestiler til fordel for lesbarhet og vedlikeholdsevne. En annen fordel med den mer ordrike formen er at det krever mindre ressurser når man programmerer til en CPLD[trenger referanse].

Syntetiserbare konstruksjoner og VHDL-maler rediger

VHDL er ofte brukt av to ulike grunner: simulering av elektronisk design og syntese av slike. Syntese er en prosess hvor VHDL blir kompilert og koblet sammen i en implementasjonsteknologi som en FPGA eller en ASIC. Mange FPGA-produsenter har gratis eller billige syntetiseringsverktøy til bruk med deres brikker, mens ASIC-verktøy ofte er veldig kostbare.

Ikke alle konstruksjoner i VHDL er egnede for syntese. De fleste konstruksjoner som bruker tidtaking, slik som wait for 10 ns;, er for eksempel ikke syntetiserbare, selv om de er lovlige under simulering. Selv om forskjellige synteseverktøy har forskjellige muligheter, eksisterer det en felles syntetiserbar delmengde av VHDL som definerer hvilke språk-konstruksjoner og -idiomer som kobles sammen til vanlig maskinvare for mange synteseverktøy. IEEE 1076.6 definerer en delmengde av språket som er sett på som den offisielle syntetiserbare delmengden. Det er som regel sett på som «god praksis» å skrive idiomatisk kode for syntese, da resultatet kan være feil eller ikke helt optimalt hvis man bruker ikke-standard konstruksjoner.

Noen kodeeksempler som kobles til multipleksere i maskinvare i vanlige verktøy:

MUX-maler rediger

Multiplekseren, eller «MUX» som den vanligvis kalles, er en enkel konstruksjon som er veldig vanlig i maskinvaredesign. Eksempelet under demonstrerer en enkel to-til-en MUX med inngang A og B, velger S og utgang X:

 
-- template 1:
X <= A when S = '1' else B;

-- template 2:
with S select X <= A when '1' else B;

-- template 3:
process(A,B,S)
begin
  case S is
    when '1'    => X <= A;
    when others => X <= B;
  end case;
end process;

-- template 4:
process(A,B,S)
begin
  if S = '1' then
    X <= A;
  else
    X <= B;
  end if;
end process;

-- template 5 – 4:1 MUX, where S is a 2-bit std_logic_vector :
process(A,B,C,D,S)
begin
  case S is
    when "00"   => X <= A;
    when "01"   => X <= B;
    when "10"   => X <= C;
    when others => X <= D; 
  end case;
end process;

De tre siste malene benytter seg av det VHDL kaller «sekvensiell» kode. De sekvensielle seksjonene er alltid plassert inni en process og har en litt annerledes syntaks som kanskje minner litt om tradisjonell programmering.

Vippe-mal rediger

En transparent vippe er i bunn og grunn et minne som holdes oppdatert så lenge «enable»-signalet ligger aktivt.

-- latch template 1:
Q <= D when Enable = '1' else Q;

-- latch template 2:
process(D,Enable)
begin
  if Enable = '1' then
    Q <= D;
  end if;
end process;

En SR-vippe bruker et «set»- og «reset»-signal i stedet for:

-- SR-latch template 1:
Q <= '1' when S = '1' else
  '0' when R = '1' else Q;

-- SR-latch template 2:
process(S,R)
begin
  if S = '1' then
    Q <= '1';
  elsif R = '1' then
    Q <= '0';
  end if;
end process;

Mal 2 har en implisitt «else Q <= Q;» som eksplisitt kan legges til hvis det er ønsket.

-- This one is a RS-latch (i.e. reset dominates)
process(S,R)
begin
  if R = '1' then
    Q <= '0';
  elsif S = '1' then
    Q <= '1';
  end if;
end process;

D-vipper rediger

D-vipper sjekker et innkommende signal på stigende eller synkende klokkeflanke. D-vippen ligger til grunn for all synkron logikk.

-- simplest DFF template (not recommended)
Q <= D when rising_edge(CLK);

-- recommended DFF template:
process(CLK)
begin
  -- use falling_edge(CLK) to sample at the falling edge instead
  if rising_edge(CLK) then
    Q <= D;
  end if;
end process;
  
-- alternative DFF template:
process
begin
  wait until rising_edge(CLK);
  Q <= D;  
end process;

-- alternative template:
process(CLK)
begin
   if CLK = '1' and CLK'event then--use rising edge, use  "if CLK = '0' and CLK'event" instead for falling edge
      Q <= D;
   end if;
end process;

Noen D-vipper har også «enable»-signaler og asynkrone eller synkrone «Set»- og «Reset»-signaler:

-- template for asynchronous reset with clock enable:
process(CLK, RESET)
begin
  if RESET = '1' then   -- or '0' if RESET is active low...
    Q <= '0';
  elsif rising_edge(CLK) then
    if Enable = '1' then  -- or '0' if Enable is active low...
      Q <= D;
    end if;
  end if;
end process;

-- template for synchronous reset with clock enable:
process(CLK)
begin
  if rising_edge(CLK) then
    if RESET = '1' then 
      Q <= '0';
    elsif Enable = '1' then  -- or '0' if Enable is active low...
      Q <= D;
    end if;
  end if;
end process;

En vanlig nybegynnerfeil er å ha en «Set»- eller «Reset»-inngang, men ikke bruke den. De to følgende kodesnuttene er ikke like, den første er en enkel D-vippe, mens den andre er en D-vippe med en tilbakekoblings-MUX.

-- simple D-type flip-flop 
process(CLK)
begin
  if rising_edge(CLK) then  
    Q <= D;  
  end if;
end process;

-- BAD VHDL: this does NOT make the flip-flop a DFF without a reset!!
process(CLK, RESET)
begin
  if RESET = '1' then  
  	-- do nothing. Q is not set here...  
  elsif rising_edge(CLK) then  
    Q <= D;  
  end if;
end process;

Dette er veldig likt feilen med den «transparente vippen» som ble nevnt tidligere.

Teller-eksempel rediger

Det følgende eksempelet er en teller som teller opp med asynkron «reset», parallell last og konfigurerbar bredde. Det demonstrerer bruken av typen «unsigned» og VHDL generics. Generics er veldig likt argumenter eller maler i andre tradisjonelle programmeringsspråk som C eller C++.

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;    -- for the unsigned type

entity counter_example is
generic ( WIDTH : integer := 32);
port (
  CLK, RESET, LOAD : in std_logic;
  DATA : in  unsigned(WIDTH-1 downto 0);  
  Q    : out unsigned(WIDTH-1 downto 0));
end entity counter_example;

architecture counter_example_a of counter_example is
signal cnt : unsigned(WIDTH-1 downto 0);
begin
  process(RESET, CLK)
  begin
    if RESET = '1' then
      cnt <= (others => '0');
    elsif rising_edge(CLK) then
      if LOAD = '1' then
        cnt <= DATA;
      else
        cnt <= cnt + 1;
      end if;
    end if;
  end process;

  Q <= cnt;

end architecture counter_example_a;

Selv om det ikke er anbefalt i nye design, kan std_logic_vector brukes i stedet for unsigned.

Mer komplekse tellere kan legge til if/then/else-setninger innenfor rising_edge(CLK) elsif for å legge til andre funksjoner som for eksempel «Enable», stopping eller nullstilling ved en gitt tellerverdi, generering av diverse utgangssignaler, etc. Man må passe på rekkefølgen på slike kontroll-setninger, slik at prioritetene blir korrekte og at man bruker et minimum av logiske nivåer.

Konstruksjoner kun for simulering rediger

En stor delmengde av VHDL kan ikke bli oversatt til maskinvare. Denne delmengden kalles for den ikke-syntetiserbare mengden, og kan bare brukes for prototyping, simulering og feilsøking. Den følgende koden vil for eksempel generere en klokke med en frekvens på 50MHz. Den kan for eksempel bli brukt til å drive en klokkeinngang i et design under simulering. Den er imidlertid en konstruksjon kun for simulering, og kan ikke implementeres i maskinvare.

process
begin
  CLK <= '1'; wait for 10 ns;
  CLK <= '0'; wait for 10 ns;
end process;

De ikke-syntetiserbare konstruksjonene kan brukes til å lage komplekse bølgediagrammer på veldig kort tid. Slike bølgediagrammer kan for eksempel brukes som testvektorer for et kompleks design eller som en prototyp av syntetiserbar logikk som vil bli implementert i fremtiden.

process
begin
  wait until START = '1'; -- wait until START is high
  
  for i in 1 to 10 loop -- then wait for a few clock periods...
    wait until rising_edge(CLK);
  end loop;

  for i in 1 to 10 loop 	-- write numbers 1 to 10 to DATA, 1 every cycle
    DATA <= to_unsigned(i, 8);
    wait until rising_edge(CLK);
  end loop;

  -- wait until the output changes
  wait on RESULT;
  
  -- now raise ACK for clock period
  ACK <= '1';
  wait until rising_edge(CLK);
  ACK <= '0';
  

  -- and so on...
end process;

Se også rediger

Eksterne lenker rediger