Oblig 2 - Design og simulering av CLA-adder i VHDL
Nedlastbar pdf - Merk introduksjonsteksten er noe forskjellig- oppgaven er ellers lik.
VHDL skiller seg fra andre programmeringsspr?k ved at det beskriver hardware, ikke software. Det vil si at VHDL koden ikke blir utf?rt p? en CPU, men i steden beskriver hvordan en krets med logiske porter kobles fysisk (blir implementert). I denne obligen er m?let ? f? ?velse i ? lage tre typer kode i vhdl: Dataflyt-, strukturell- og testbenkkode. Dataflytkode beskriver sammenkobling av logiske porter (AND, OR, XOR, osv). Blokker med logikk kaller vi ofte for moduler. Koden som kobler disse modulene sammen til st?rre systemer kalles strukturell kode. Alle systemer man lager m? testes (verifiseres) at fungerer som det skal. Sm? systemer kan testes direkte i et simuleringsverkt?y, men for st?rre system er det alltid gunstig ? automatisere testingen med en testbenk. Koden vi skriver testbenken i er VHDL, men den beskriver en sekvens av hendelser (omtrent som annen software*) i tillegg til ? beskrive eventuelle kretser vi trenger sammen med den vi skal teste. I enkleste forstand leser man av hvordan signalene inn og ut av kretsen endrer seg i waveform-vinduet til simuleringsverkt?yet, etter ? ha kj?rt testbenken. Vi kan ogs? skrive tesbenkkode som sjekker at utgangene til kretsen gir de resultatene vi forventer underveis og rapporterer til skjerm eller fil (selvsjekkende testbenker).
[* Det finnes verkt?y som lar oss skrive testbenker i vanlige programmeringsspr?k som for eksempel Python, men det er ikke pensum i dette kurset]
I denne obligen skal vi stegvis implementere en carry-lookahead adder(CLA-adder
). Dette vil gi oss erfaring med alle typene kode nevnt ovenfor, i tillegg til ?velse i ? bruke et simuleringsverkt?y og lese av waveform-vindu. Til sist er det et m?l ? ?ke forst?elsen for oppbygningen av digital elektronikk generelt, slik at det blir lettere ? analysere hvordan de virker.
Oppgave 1 - Halvadder
M?let med denne oppgaven er ? bli kjent med hvordan vi benytter simuleringsverkt?y til ? analysere kretser ved utvikling av hardware. Verkt?yet vi bruker heter Questa (tidligere Modelsim) og er et kompilerings- og simuleringsverkt?y for hardwarespr?k (b?de VHDL og verilog). Vi skal bli kjent med Questa ved ? simulere en halvadder.
- Lag en mappe p? UIO-hjemmeomr?det ditt, og lag en fil i denne mappen med navn
halvadder.vhd
. - VHDL koden for en halvadder er gitt nedenfor. Skriv denne koden inn i filen du nettop laget.
library IEEE; -- bibliotek
use IEEE.STD_LOGIC_1164.all; -- pakke
entity halfadder is
port(
a, b : in std_logic; -- inputs
s : out std_logic; -- sum
c : out std_logic -- carry
);
end entity halfadder;
architecture dataflow of halfadder is
begin
s <= a xor b
c <= a and b;
end architecture dataflow;
N?r du har lagret filen, skal du ?pne den i Questa. Questa er installert p? linux-systemet til ifi. For ? benytte Questa, m? linuxmilj?et ditt settes opp slik at lisensiering fungerer og at systemet kjenner stiene som er n?dvendige for ? kj?re programmet. Hvordan du setter opp dette er beskrevet p? siden FPGA_tools
p? wikien til robin.
N?r milj?et er satt opp kan du kj?re vsim
i terminalen for ? starte Questa.
I questa:
- Velg
File -> New -> Project
i menyen ?verst til venstre for ? lage et nytt prosjekt. - Sett mappen der du lagret
halvadder.vhd
somProject Location
, og gi prosjektet et navn f?r du klikker p?OK
. - Velg
Add existing files
, og legg tilhalvadder.vhd
. Senere kan du h?yreklikke i prosjektvinduet og velgeAdd to project
for ? legge til eller opprette filer. - Kompiler filen ved ? h?yreklikke i prosjektvinduet og velge
Compile -> Compile All
.
Det er lagt inn en feil i halvadderkoden, som gj?r at kompileringen feiler. Dette vises med et r?dt kryss ved siden av filnavnet i prosjektvinduet. Du kan se feilmeldingene ved ? h?yreklikke i prosjektvinduet og velge Compile -> Compile Summary
. Fiks alle feil inntil filen kompilerer. Dette vises med en gr?nn hake ved siden av filnavnet. N?r filen er kompilert kan vi begynne simuleringen. Vi skal f?rst se p? simulering uten testbenk.
- Velg
Simulate -> Start simulation
. I vinduet som kommer opp, utvid mappen work, og velghalfadder
. - Sjekk at
Enable optimization
er p?, og trykk p?Optimization Options
. Under fanenVisibility
velgApply full visibility to all modules (full debug mode)
og trykk OK og s? OK igjen. Simuleringsvinduet vil n? ?pnes (kan ta litt tid). - Legg til signalene
a
,b
,s
, ogc
, fra halvadderen, i waveform vinduet. H?yreklikk p? signalene i det m?rkebl? vinduet, og velgadd wave
. - Simuler halvadderen i 100 nanosekunder ved ? skrive
run 100 ns
iTranscript
vinduet. H?yreklikk i det sorteWave
vinduet, og velgZoom Full
for ? zoome ut slik at du ser resultatet av hele simuleringen.
I Wave
vinduet, vil du n? se fire r?de streker, en for hvert signal, samt at det st?r U i kolonnen Msgs ved siden av signalnavnene. Det betyr at den gule linja (cursoren) st?r et sted der signalene er uninitialized
(derav U). For ? f? noe vettugt ut av et digitalt design, m? vi sette inputene til en verdi.
- H?yreklikk p? a signalet i wave-vinduet, velg ?Force? og sett verdien (value) til 0.
- Gj?r det samme for b signalet.
- Kj?r simuleringen i et mikrosekund (run 1 us), og zoom ut slik at du ser resultatet av hele simuleringen.
P? dette tidspunktet b?r alle signalene kunne leses av som 0.
- Test mulighetene som f?lger p? samme m?te som testen ovenfor:
- Sett a til 1 og b til 0 og kj?r ett mikrosekund.
- Sett s? a til 0 og b til 1 og kj?r ett mikrosekund.
- Til slutt sett b?de a og b til 1 og kj?r ett mikrosekund.
- Zoom ut igjen, og sjekk at du f?r riktige verdier for sum (s) og carry (c).
- Lag en bildekopi av Waveformvinduet til rapporten:
- Med Wave vinduet aktivt (aktiver ved ? trykke i vinduet):
- I den ?verste menyen til Questa, trykk File->Export->image
- Skift bildetype til png
- Lagre bildet som halfadder.png
- Med Wave vinduet aktivt (aktiver ved ? trykke i vinduet):
Innleveringen i oppgave 1 skal best? av bildefilen halfadder.png.
Oppgave 2 - Fulladder og testbenk
Skriv en VHDL modul for en fulladder som vist p? Figur 2 nedenfor. Symbolet for en fulladder er vist i Figur 3. Modulen skal hete fulladder.vhd, og entiteten skal hete fulladder. Bruk navnene a, b og cin for inputene, og s og cout for outputene. Alle inputs og outputs skal v?re av typen std_logic.
Sammenhengen mellom input og output for fulladderen skal v?re som angitt i sannhetsverditabellen:
a | b | cin | cout | s |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | 1 |
0 | 1 | 0 | 0 | 1 |
0 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 0 | 1 |
1 | 0 | 1 | 1 | 0 |
1 | 1 | 0 | 1 | 0 |
1 | 1 | 1 | 1 | 1 |
- Kompiler koden og rett eventuelle feil.
For at testingen av disse verdiene skal g? smidig har vi laget en testbenk for fulladderen:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity tb_fulladder is -- testbenkentiteter er normalt tomme.
end entity tb_fulladder;
architecture behavioral of tb_fulladder is
-- en komponent er en entitet definert i en annen fil, og som vi vil bruke.
-- komponentdeklarasjonen m? matche entiteten.
component fulladder is
port(
a, b : in std_logic;
cin : in std_logic;
s : out std_logic;
cout : out std_logic
);
end component;
-- Tilordning av startverdi ved deklarasjon gj?res med :=
signal tb_a, tb_b, tb_cin : std_logic := '0';
-- outputs b?r ikke f? en startverdi i testbenken, da det kan maskere feil.
signal tb_s, tb_cout : std_logic;
begin
-- instansiering:
DUT: fulladder -- Merkelappen DUT betyr ?device under test? som er en av mange
port map( -- vanlige betegnelser p? simuleringsobjektet.
a => tb_a, -- Mappinger gj?res med =>, til forskjell fra tilordninger som
b => tb_b, -- bruker <= eller :=
cin => tb_cin, -- Mappinger kan ses p? en ren sammenkobling av ledninger
s => tb_s, -- Vi mapper alltid testenhetens porter til testbenkens signaler
cout => tb_cout -- Siste informasjon f?r parantes-slutt har ikke ',' eller ';'
);
-- I testbenker kan vi ha prosesser uten sensitivitetsliste..
-- i slike prosesser kan vi angi tid med ?wait? statements, og
-- vi kan sette signaler flere ganger etter hverandre uten ? gi konflikter.
-- NB: Prosessen vil trigges om og om igjen om vi ikke hindrer det.
process
begin
wait for 10 ns;
tb_a <= '1';
tb_b <= '0';
tb_cin <= '0';
wait for 10 ns;
report("Ferdig!") severity note;
std.env.stop; -- stopper simuleringen
end process;
end architecture behavioral;
- Lag en fil tb_fulladder.vhd, der du skriver inn koden fra ovenfor. Modifiser koden slik at du f?r testet alle mulighetene angitt i sannhetsverditabellen, med 10 ns mellomrom.
- Kompiler og rett opp eventuelle feil
- Simuler med testbenken:
- N?r du simulerer med en testbenk m? modulen du skal teste v?re kompilert f?r du starter simuleringen.
- Start simuleringen med Simulate->Start simulation->Work->tb_fulladder.
- Trykk p? optimization options -> customized visibility -> add -> work -> tb_fulladder -> ok. (Om man ikke gj?r dette vil hele testbenken optimaliseres bort)
- I sim-fanen i simuleringen, ekspander tb_fulladder, og velg DUT.
- Legg til alle signalene til fulladderen i waveformen.
- I transcript vinduet skriv: run –all
N?r simuleringen stopper vil modelsim automatisk hoppe inn i koden der den stoppet. Vanligvis kommer dette vinduet opp foran wave-vinduet. For ? f? frem wave-vinduet igjen, kan man enten lukke vinduet med koden, eller trykke p? wave-tab’en som dukket opp i underkant av vinduet.
- Zoom ut fullt, og kontroller resultatet.
- Eksporter waveformen til en png-fil med navn fulladdertest.png og en vcd-fil med navn fulladdertest.vcd. Alle signalene m? v?re valgt ved eksport av vcd.
Innleveringen i oppgave 2 skal best? av filene: fulladder.vhd, tb_fulladder.vhd, fulladertest.png og fulladdertest.vcd.
Oppgave 3 - CLA-blokk med testbenk
Carry-lookahead adder (CLA)
Carry-lookahead addere er beskrevet i kapittel 5.2 i l?reboka (s241). I denne oppgaven skal vi lage en 32-bits CLA adder og teste den med simulering. Det er ikke n?dvendig ? forst? hvorfor CLA adderen vil virke for ? fullf?re denne oppgaven, men vi h?per oppgaven kan hjelpe til ? avmystifisere hvordan den er bygget opp.
Innholdet i CLA blokken er vist p? to m?ter i Figur 4 og Figur 5.
Utgangspunktet for designet er figur 5.6 p? side 242 i l?reboka. Vi deler CLA adderen i 3 forskjellige designmoduler. Den innerste modulen har du allerede laget. Det er fulladderen fra oppgave 2. Videre lager vi i denne oppgaven en modul for CLA-blokken. CLA blokken skal danne logikken beskrevet i den bl? stiplede firkanten i Figur 4/5, og vil da legge sammen to 4-bit tall. CLA blokken vil benytte fulladderen, og vil derfor ha b?de strukturell- og dataflytkode. Etterp?, i neste oppgave, setter vi sammen flere CLA blokker i en modul som skal hete CLA_top, slik at vi kan legge sammen tall som er st?rre enn 4 bit. Hver designmodul skal ha sin egen testbenk.
entity CLA_block is
port(
a, b : in std_logic_vector(3 downto 0);
cin : in std_logic;
s : out std_logic_vector(3 downto 0);
cout : out std_logic
);
end entity CLA_block;
Bruk entitetsbeskrivelsen for CLA_block ovenfor, og skriv og kompiler CLA-blokk-modulen:
- Filen skal hete cla_block.vhd
- Arkitekturen skal hete ?mixed? siden du skal benytte en kombinasjon av strukturell- og dataflyt-kode.
- Inkluder fulladdermodulen som en komponent.
- Lag egne signaler for ?propagate? (p) og ?generate? (g). Disse signalene skal v?re av typen std_logic_vector, og skal ha samme antall bit som a og b.
- Tilordne verdier til ?p? og ?g? slik at vi f?r
- p(i) = a(i) OR b(i)
- g(i) = a(i) AND b(i).
- Lag et carry-signal ?c? av typen std_logic_vector, la antall bit v?re 5 (fra 4 ned til 0).
- Lag et eget signal ?p30? og ?g30? for ? beregne ?3:0og ?3:0 i henhold til Figur 5.
- Bruk p30 og g30 til ? beregne cout.
- Hint: bruk en reduksjonsoperator for p30
- Hint: bruk paranteser for ? holde orden p? g30
- Sett c(0) til cin.
- Opprett (instansier) de fire fulladderne og koble dem med port maps til signalene a, b og c.
- Velg én av disse to metodene ? gj?re det p?:
- Enten: Gi hver av de fire fulladderne en egen merkelapp, instansier dem, og koble til portene med port-mapping. (Som instansieringen av fulladderen i tb_fulladder).
- Eller (vanskeligere): I VHDL kan man bruke l?kker med for + loop, eller for + generate. Bruk av l?kker gir kompakte l?sninger som skalerer bedre enn om man skriver ut l?sningen manuelt. N?r man oppretter komponenter brukes generate (ved bruk av eksisterende komponenter og signaler brukes loop.). L?s oppgaven ved ? benytte for … generate. Et eksempel p? bruk av for + generate er vist nedenfor.
- Velg én av disse to metodene ? gj?re det p?:
min_l?kke: for i in 1 to 5 generate ny_komponent: min_komponent
port map(
epler => eplesignal(i),
p?rer => p?resignal(i)
);
end generate;
Lag en testbenk for ? teste CLA_blokken. Testbenken skal hete tb_cla_block.vhd. Benytt assert slik at testbenken stopper ved feil.
Eksempel p? assert
-statement:
assert(i = j) report ("i er ulik j") severity failure;
assert
rapporterer n?r det boolske uttrykket inni parantesen er usant. Alvorlighetsgraden (severity
) har fire niv?er: note
, warning
, error
, failure
. Bare failure
stopper simuleringen. Report
kan ogs? brukes alene, uten assert
, og vil da alltid skrive ut beskjeden.
Velg én av metodene under:
- Enten: Lag den nye testbenken ved ? kopiere og modifisere testbenken fra oppgave 2, slik at den passer for CLA blokken. Velg ut minst 6 tallpar som du legger sammen med CLA blokken. S?rg for at noen vil lage mente (carry)- og noen ikke gj?r det. Bruk assert statement til ? teste om resultatene fra CLA-blokken er riktig. Bruk ?severity failure? ved feil. Etter alle testene er gjennomf?rt skal testbenken gi en tekstbeskjed om testen foregikk feilfritt.
- Eller (Vanskelig): Test at CLA modulen oppf?rer seg slik den skal ved ? lage en testbenk som g?r igjennom alle muligheter for a, b og cin.
- Bruk for …+ loop for ? lage alle verdiene for a, b og cin.
- Bruk assert for ? sjekke at verdiene stemmer, bruk ?severity failure? ved feil.
- Bruk report for ? rapportere n?r testbenken er ferdig.
- Hint (for begge alternativene):
- CLA blokken tar inn to fire bits signaler (a og b), samt carry (cin). Testbenken m? sette disse signalene og sjekke verdiene p? summen (s) og carry out (cout).
- For at simuleringen skal vise forskjellene, m? du vente mellom hver gang du setter og tester noe. Du b?r vente noenlunde like lenge hver gang, slik at du kan sjekke resultatet p? waveformen.
Innlevering i oppgave 3 skal best? av VHDL filene cla_block.vhd, tb_cla_block.vhd og en tekstfil med kopi av outputen i konsollvinduet for siste kj?ring.
Oppgave 4 - CLA-toppmodul med strukturell kode og testbenk
entity CLA_top is
generic(
width : positive := 32;
);
port(
a, b : in std_logic_vector(width-1 downto 0);
cin : in std_logic;
sum : out std_logic_vector(width-1 downto 0);
cout : out std_logic
);
end entity CLA_top;
Bruk entitetsbeskrivelsen ovenfor og lag og kompiler toppmodulen slik at den kan utf?re 32 bits addisjon. Filnavnet p? toppmodulen skav v?re cla_top.vhd. CLA_top skal benytte ?tte CLA-blokker til ? utf?re beregningen. Du velger selv hvordan du instansierer komponentene.
Lag en testbenk som tester ut 10 forskjellige tallkombinasjoner etter tur og rapporterer eventuelle feil. Tallkombinasjonene b?r inneholde noen tall som bruker, og noen som ikke bruker, de mest signifikante bitene i a og b. Eksporter waveformen som en vcd fil CLA_top.vcd som viser kj?ringen med alle input og output.
Innleveringen i oppgave 4 skal best? av toppmodulen cla_top.vhd, testbenken tb_cla_top.vhd og en waveform CLA_top.vcd som viser kj?ringen med alle verdiene for input og output.
Hint (valgfritt ? benytte):
I VHDL kan man ikke uten videre oversette mellom tall (integer) og andre typer slik som std_logic, men det finnes biblioteker som kan gj?re det.
For tallkonverteringer benytter vi biblioteket IEEE.numeric_std
Her er et eksempel p? deklarering av biblioteket:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.numeric_std.all;
Hvis vi skal konvertere et heltall til en std_logic_vector, m? vi f?rst bestemme oss om vi vil ha med fortegnsbit eller ikke. For ? benytte fortegn har biblioteket en type som heter signed, mens uten fortegn har vi unsigned. Disse typene best?r av std_logic_vectors, og man m? angi bitbredden p? samme vis. N?r vi konverterer et tall til f.eks unsigned, s? m? funksjonen vi bruker ha beskjed om hvor mange bit vi skal ha. Her f?lger eksempel p? konvertering fra integer til std_logic_vector og fra std_logic_vector til integer uten fortegn:
signal my_sig : std_logic_vector(31 downto 0);
signal my_int : integer;
...
my_sig <= std_logic_vector(to_unsigned(my_int,32));
my_int <= to_integer(unsigned(my_sig));