Högskolan Dalarna Elektroteknik LABORATION LABORATIONSINSTRUKTION Räknare och skiftregister med sekvensiell VHDL KURS Digitalteknik LAB NR Ver09 INNEHÅLL. Strukturell VHDL. Sekvensiell VHDL 3. Strukturell VHDL med grafiskt gränssnitt. Återkopplade skiftregister 5. Seriell 8-bitars dataöverföring NAMN KOMMENTARER PROGRAM/KURS UTFÖRD GODKÄND SIGN
Laboration nr Digitalteknik Innehåll: Syfte: Strukturell och sekventiell VHDL Att få kunskap och praktisk efarenhet av: grafiskt gränssnitt för strukturell VHDL de vanligaste instruktionerna i sekvensiell VHDL kombinatoriska och synkrona processer ivhdl en generator för binära slumptal att studera seriell dataöverföring simulering av digitala konstruktioner realisering (programmering) av VHDL-konstruktioner i en CPLD dokumentation Strukturell VHDL De kretsar som hittills har beskrivits i laboration och 3 har bestått av enbart en modul med VHDL-kod. Med bara en modul är det svårt att strukturera konstruktionen och det är också svårt att återanvända koden. Att använda flera moduler är därför nödvändigt och det görs i princip alltid vid konstruktioner med VHDL. Modulerna innebär också att man kan göra hierarkiska konstruktioner d.v.s. att konstruktionen består av olika nivåer. För att kunna använda flera moduler i samma beskrivning gör man om dem till en komponent (component) som kan sparas i komponentbibliotek och användas i olika tillämpningar. När man använder komponenter gör man först kopior av modulen genom instanser (instance) och förbinder (port map) sedan kopiorna med varandra. I läroboken finns exempel på detta på sid 0-3 och normalt använder man alltid den metoden. Med Webpack kan man använda ett grafiskt gränssnitt för att göra exakt samma sak och det är om möjligt ännu enklare. En nackdel med metoden är att det inte är lika lätt att flytta konstruktionen mellan olika miljöer men om man bara använder samma miljö så fungerar den alldeles utmärkt. Med det grafiska gränssnittet förbinder man komponenterna med trådar precis som när man ritar ett kopplingsschema. De externa in- och utgångarna markeras med markörer för input/output och därefter kan hela konstruktionen syntetiseras på vanligt sätt. Metoden är demonstrerad på föreläsning och i korthet gör man följande: Skriv separata moduler och syntetisera dem på normalt sätt. Spar grafiska symboler för modulerna med Create Schematic Symbol Skapa en ny källkodsfil med Project/New Source/Schematic/. Genom kommandot öppnas ett tomt dokument i den grafiska editorn. Markera Symbols/<project name> Till vänster på bildskärmen visas vilka symboler som finns tillgängliga och där finns symboler för de egna modulerna. Placera symbolerna på ritytan med musen (motsvarar instansiering) Förbind symbolerna med pennan och tryck-drag-släpp med musen (motsvarar port map) Anslut korta trådar på alla in- och utgångar Anslut Input Marker / Output marker på alla in- och utgångar Namnge näten (klicka på abc-symbolen, skriv ett signalnamn, klicka på tråden) Spara det färdiga schemat och stäng den grafiska editorn Syntes av Schemat görs därefter på samma sätt som de separata modulerna. Observera att det finns nya markeringar i Sources for Project som visar att modulerna tillhör det överordnade schemat. Lab - - pls
Sekvensiell VHDL Sekvensiell VHDL innebär att instruktionerna i VHDL-koden utförs i sekvens där den första instruktionen görs först, sedan nästa osv. Eftersom instruktionerna fortfarande ska motsvara hårdvara som är parallell till sin natur och inte kan beskrivas i tidsordning har man gjort på ett speciellt sätt. Metoden är att den sekvensiella koden skrivs i processer som antingen är passiva eller aktiva. Processen blir aktiv genom att en av parametrarna till processen förändras. När en process är aktiv stoppas den verkliga tiden och instruktionerna genomförs sekvensiellt utan att den verkliga tiden förändras. I processen förändras signalvärden och värdena tilldelas signalvariablerna när processen avslutas. Om en utsignal inte har fått ett nytt värde så behåller signalen det gamla värdet genom att en minnesfunktion (latch/vippa) har skapats. Med sekvensiell VHDL kan flera instruktioner användas och tabellen nedan ger en sammanställning av de flesta av instruktionerna och när de kan användas: Parallell VHDL signal declaration (*) process statement (*) when else statement (*) with select statement(*) Sekvensiell VHDL variable declaration variable assignment if-then-else statement(*) case statement(*) loop statement return statement null statement wait statement Gemensamma signal assignment (*) declaration of types and constants function and procedure calls assert statement after delay statement signal attributes (*) markerar de instruktioner som används mest i labkursen i digitalteknik Observera att process statement är en parallell instruktion vilket innebär att alla processer exekveras samtidigt. Signaler deklareras i den parallella delen men är även åtkomliga i den sekvensiella delen. Variabler kan vara praktiska att använda ibland men oftast räcker det med signaler. Att använda processer i VHDL är mycket vanligt och det förenklar beskrivningarna i hög grad. Det är mycket viktigt är att få en känsla för hur processerna fungerar och man brukar skilja mellan kombinatoriska processer och synkrona processer. En process består av: process(sensitivity_list) process_deklaration.. Lab - - pls
Kombinatoriska processer I kombinatoriska processer måste alla insignaler till processen finnas med i sensitivity list. Det är inte säkert att det blir fel om någon signal utelämnas men det kan bli skillnader mellan simulering och verklighet. Naturligtvis måste alla utsignaler tilldelas värden annars uppstår minnesfunktioner för de signaler som inte får värden. När man använder if-satser är det lätt att glömma det. Exempel på en kombinatorisk process: process(a,b,c) variable temp: std_logic; temp:=a or b; ut<=temp and not c; I exemplet är en variabel deklarerad och använd på ett sätt som kan förenkla kodningen. Observera att variabeln temp i det här fallet är av typen std_logic och att tilldelningen sker med en annan operator (:=). Synkrona processer Synkrona processer styrs av flanken på en klocksignal. Sensivity list måste innehålla klocksignalen och andra direktverkande signaler (tex reset). Här är det fel att ta med alla insignaler eftersom då försvinner den synkrona klockningen. Exempel på en synkron process: process (clk,resetn) if resetn = 0 then q<= 0 ; elsif clk event and clk = then q<=d; Exemplet visar koden för en positivt flanktriggad D-vippa med direktverkande resetsignal. Kommentarer: processen startar om clk eller resetn i sensivity list förändras. Om resetn blir är inget av villkoren uppfyllda och föregående värde (alltså minnesfunktion) behålles. Om resetn blir 0 blir q nollställd och inga fler satser utförs. När clk blir en etta, (positiv flank, clk event, clock tic event) blir villkoret efter elsif true och q tilldelas värdet av d. Event är ett attribut till signalen clk och returnerar true om det finns en flank på clk, annars false. Uttrycket är lite märkligt men det är vanligt skrivsätt för positiv flank. I läroboken används rising_edge(clk) som gör samma sak. Lägg märke till end if och elsif. I synkrona processer för t.ex. räknare och register behöver man ofta använda det aktuella värdet både som insignal ( läsning ) och för att ge nya utdata ( skrivning ). De signaler som man normalt använder är deklarerade i entity (externa signaler) och är av typ out (skrivning) eller in (läsning) men kan inte användas för båda operationerna. Ofta ersätter man de externa signalerna med kopior, dummy-signaler, som deklareras direkt under architecture. Kopian är både läs- och skrivbar och används internt i processen. När processen är avslutad överförs kopiornas värden till de externa signalerna i en parallell sats utanför processen. I källkoderna i slutet av lab-pm används kopior enligt denna metod. Kopiorna, dummy-signalerna, är betecknade med <name>_b. OBS signaler av typen inout får inte deklareras i entity i detta fall. Lab - 3 - pls
If statement Syntaxen för if-satsen är: if <condition> then <sequence of statements> [elsif <condition> then <sequence of statements>] [elsif <condition> then <sequence of statements>]... [else <sequence of statements>] varje statement avslutas med semikolon; <condition> (villkor) ger värdet true/false och uttrycks med relationsoperatorerna = /= < <= > >= bara ett av elsif kan utföras else är villkorslöst och kan utelämnas Med if-satsen är det enkelt att konstruera val mellan flera olika alternativ där bara ett av alternativen blir utfört Case statement Syntaxen är för case-satsen är: case <expression> is when <choice> => <sequence of statements> when <choice> => <sequence of statements>. [when others => [<sequence of statements>];] end case; choice kan bestå av flera villkor med operatorn (or) I <expression> erhålles ett värde som styr vilken when sats som kommer att utföras. Exempel på användning av case-satsen: process(data_in,a) case a is when 00 => data_ut <= data_in(0); when 0 => data_ut <= data_in(); when 0 => data_ut <= data_in(); when => data_ut <= data_in(3); when others => null; end case; Exemplet beskriver en MUX - med hjälp av en kombinatorisk process och case-satsen. Lab - - pls
3 Strukturell VHDL med grafiskt gränssnitt I figuren nedan visas en 7-segmentdisplay som är ansluten till en dekadräknare och en frekvensdelare. Dekadräknaren räknar uppåt och med ett steg i sekunden och räknevärdet syns på displayen längst till vänster på utvecklingskortet för CoolRunner. Siffrorna i figuren visar anslutningsstiften på utvecklingskortet. clk BTN 38 9 CoolRunner-II CPLD modul clk_div clk resetn en_clk modul cnt_bcd en clk resetn modul hexled 56 53 60 58 57 5 8*00Ω 6 (59) a b c d e f g dp f e 30.kΩ a g d b c +3.3V Konstruktionen är gjord i tre VHDL moduler som är sammankopplade med hjälp av det grafiska gränssnittet i WebPack. Modulerna är: clk_div 0 bitars binärräknare, dvs ä insignaler clk MHz systemklocka resetn resetsignal, aktiv på låg nivå utsignal en_clk har värdet varje gång räknaren har nått sitt slutvärde. cnt_bcd BCD räknare, räknar 0,,,9,0,, insignaler en aktiverar kretsen, enable clk MHz systemklocka resetn resetsignal, aktiv på låg nivå utsignaler fyra bitar som ger BCD-koden, ex 00 ska ge siffran 9 på displayen. hexled Omvandlar BCD-koden till 7 segmentkod, se föregående laboration. Observera att modulen även ger utsignaler till de fyra transistorerna som driver respektive sifferdisplay Observera att BCD-räknaren styrs av systemklockan och att den görs enable ca gång i sekunden via binärräknaren. Alla kretsar i en sekvenskrets bör (måste) klockas av exakt samma klocksignal. VHDL-koderna för de två räknarmodulerna finns sist i laborationen. Kopiera, förstå. dp Uppgift: Skriv (kopiera)vhdl kod för:modulerna clk_div, cnt_bcd och hexled. Syntetisera modulerna och omvandla dem till schemasymboler med Create Schematic Symbol. Koppla samman modulerna med hjälp av det grafiska gränssnittet enligt figuren ovan. Visa att konstruktionen fungerar i verkligheten. Redovisa med schema från Schematic, tidsfördröjning (från Timing Report), antalet använda makroceller etc (från Fitter Report) och eventuellt egna kommentarer. Redovisningen skall vara i skriven i ett Word-dokument (använd gärna typsnittet Courier på passande ställen) Tips Schemat för konstruktionen kan kopieras från schemaeditorn med tex Snipping Tool (gratisprogram) för Windows. Lab - 5 - pls
Uppgift: Modifiera koden för räknaren och 7-segmentindikatorna så att den styrs av ytterligare en signal even. Funktionen skall vara: even = => uppåträkning av enbart de jämna talen (0,,,6,8,0,,.) even =0 => uppåträkning av enbart de udda talen (,3,5,7,9,,3..) när even förändras skall räkningen i den nya sekvensen fortsätta med det närmast högre värdet. Tips: En binärräknare eller en dekadräknare är lätt att beskriva med +-operatorn eftersom den har en enkel räknesekvens. För den modifierade räknaren enligt uppgiften blir det svårare och det är bättre att göra på något annat sätt. Prova t.ex. med en case-sats. case c_b is when 0000 => if even= then c_b<= 000 ; else c_b<= 000 ; osv Visa att konstruktionen fungerar i verkligheten. Redovisa med källkod för den modifierade räknaren, tidsfördröjning (från Timing Report), antalet använda makroceller etc (från Fitter Report) och eventuellt egna kommentarer. Redovisningen skall vara i skriven i ett Word-dokument (använd gärna typsnittet courier på passande ställen) Lab - 6 - pls
Återkopplade skiftregister, frivillig uppgift Återkopplade skiftregister enligt figuren nedan kallas linjära sekvenskretsar och kan användas för att generera så kallade pseudoslumpsekvenser (PRBS, Pseudo Random Binary Sequence). På serieutgången erhålles en sekvens av ettor och nollor i en skenbart slumpmässig följd och med tillräckligt stort register kan man skapa riktiga slumptal. Ett av kraven på en riktig slumptalsföljd är att den är oändligt lång och för ett n-bitars skiftregister blir den maximala längden n - slumptal. Med 3 bitar (n=3) i skiftregistret blir slumptalsföljden drygt miljarder bitar lång. Binära slumptal är mycket användbara och kan användas för t.ex. kodning av datatrafik eller för att styra frekvenshopp vid radiotrafik. resetn Återkopplat skiftregister resetn Återkopplat skiftregister clk serie in q3 q q q0 serie ut (=q0) clk serie in q3 q q q0 serie ut (=q0) = = Din = Kodad dataöverföring DX = Dut Figuren ovan visar en sändare och en mottagare för en dataförbindelse som använder kodning med hjälp av återkopplade skiftregister. För att sändare och mottagare ska fungera måste de generera exakt samma slumptalsföljd på utgångarna q0. För att få samma slumptalsföljd måste de startas på samma ställe i slumptalsföljden och sedan styras av samma klocksignal. Signalen DX är den kodade signalen på dataöverföringen och den kommer att vara svår att tolka om man inte känner till kodningen. Uppgift: Skriv kod för en modul med VHDL som beskriver ett återkopplat skiftregister enligt figuren. Modulen kan även innehålla den nedre XOR-grinden eftersom den är kopplad på samma sätt i både sändare och mottagare. Gör två instansieringar med det grafiska gränssnittet och förbind dem med varandra. Simulera och försök att verifiera konstruktionen. Programmera en CPLD med konstruktionen och testa den i verkligheten. Anslut en switch till Din och lysdioder till DX och Dut. Klocka skiftregistren med en studsfri kontakt och kontrollera att den kodade dataöverföringen fungerar. Redovisa på samma sätt som föregående uppgift. Lab - 7 - pls
5 Seriell 8-bitars dataöverföring, frivillig uppgift Uppgift: Kopplingsschemat på nästa sida visar en koppling som ska göra en dataöverföring genom att: läsa in 8 databitar parallellt till ett skiftregister (den vänstra SRG8-kretsen) överföra data seriellt till ett annat skiftregistret (den högra SRG-kretsen) spara det överförda databitarna i ett 8 bitars dataregister (LS377) överföringen skall styras av en räknare med åtta räknelägen (CTR) Naturligtvis finns det ett litet problem med dataöverföringen och problemet är att den inte fungerar som det är tänkt. Det finns åtminstone ett logiskt fel i kopplingen! För att slippa göra en koppling med de gamla kretsarna så finns motsvarande funktioner beskrivna i VHDL-kod och tanken är att de ska kopplas samman med hjälp av det grafiska gränssnittet och testas i en CPLD. Troligen är det bättre att göra en helt ny konstruktion i VHDL men i det här fallet ska den gamla konstruktionen återskapas. Naturligtvis kan det hända att VHDL-koden innehåller nya fel.. Koden för VHDL-modulerna finns på kursen hemsida. Nämn någon väsentlig fördel med seriell överföring jämfört med parallell: Vad kan det finnas för nackdel med en seriell dataöverföring? Uppgift: Undersök VHDL-modulerna separat och verifiera att de har rätt funktion d.v.s. att de gör samma sak som de gamla kretsarna i kopplingen. Funktionen för kretsarna i kopplingen är: SRG8 är 8 bitars skiftregister. Om SH/LD är skiftas innehållet neråt i figuren. Om SH/LD är 0 skiftas 8 data bitar in parallellt. I det högra registret är OE aktiverad och data finns tillgängligt på de kombinerade in/utgångarna. CTR är en räknare som räknar 8,9,,5,8, När räknaren har värdet 5 sätts utgången CT=5 till. LS377 består av 8 D-vippor som är klockade med en gemensam klocksignal. Klockning kan enbart ske om enable-ingången G är aktiverad Redovisa med rättad kod och simulering som visar att modulerna är riktiga. Frivillig uppgift: Testa funktionen på dataöverföringen och rätta det ursprungliga felet. /Räknaren LS6 används som en modulo-8 räknare för att ge ramsynkroniseringspulser till sändare och mottagare så att dessa vet när hela dataordet om 8 bitar har blivit överfört. Då alla åtta bitarna har mottagits i det mottagande skiftregistret skall byten läsas över till utgångsdataregistret LS377 som fryser datat tills en ny byte har överförts. Överföringen upprepas cykliskt varför seriekanalen utnyttjas till 00%./ Lab - 8 - pls
In +5V CL +5V 0V 0V A B C D E F G H 8 9 8 9 3 7 6 5 5 6 7 3 LS3 SRG8 OE SH/LD RE R SE DS D0 D +5V CL 0V +5V 0V 0V 0 3 5 6 7 8 9 8 9 3 7 6 5 5 6 7 3 LS3 SRG8 OE SH/LD RE R SE DS D0 D SH/LD CL +5V 0V +5V 9 0 7 3 5 6 LD ENT ENP R LS6 CTR CT=5 8 5 3 CL 0 3 5 6 7 3 7 8 3 7 8 LS377 C G D D D D D D D D 5 6 9 5 6 9 A' B' C' D' E' F' G' H' Notera att LS377 har en enable -ingång som måste vara aktiverad för att klocksignalen skall ha någon verkan på registret. CL 5 8 9 0 3 5 8 9 0 SH/LD Seriekanal B A H G F E D C B A H G F Lab - 9 - pls
Filer till Seriell 8-bitars dataöverföring library IEEE; use IEEE.STD_LOGIC_6.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ctr_wrong is port(clk,resetn: in std_logic; ct5n:out std_logic); end ctr_wrong; architecture Behavioral of ctr_wrong is signal count_b: std_logic_vector(3 downto 0); process (clk,resetn) if resetn='0' then count_b <= "000"; elsif clk'event and clk='' then if count_b = "" then ct5n<='0'; count_b <= "000"; else ct5n<=''; count_b <= count_b + ; end Behavioral; library IEEE; use IEEE.STD_LOGIC_6.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity LS377_right is Port (clk,gn,resetn: in std_logic; d_in: in std_logic_vector(7 downto 0); d_out: out std_logic_vector(7 downto 0)); end LS377_right; architecture Behavioral of LS377_right is process (clk,resetn) if resetn='0' then d_out <= (others=>'0'); elsif clk'event and clk='' then if gn='0' then d_out<=d_in; end Behavioral; Lab - 0 - pls
library IEEE; use IEEE.STD_LOGIC_6.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity SRG8_left_wrong is Port (d_in: in std_logic_vector(7 downto 0); shift_loadn, clk, resetn: in std_logic; d_ut: out std_logic); end SRG8_left_wrong; architecture Behavioral of SRG8_left_wrong is signal d_reg: std_logic_vector(7 downto 0); process (clk,resetn) if resetn='0' then d_reg<="00000000"; d_ut<=d_reg(7); elsif clk'event and clk='' then if shift_loadn='0' then d_reg<=d_in; d_ut<=d_reg(7); else d_reg(7 downto )<= d_reg(6 downto 0); d_reg(0)<='0'; d_ut<=d_reg(7); end Behavioral; library IEEE; use IEEE.STD_LOGIC_6.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity srg8_right_maybe_wrong is Port (d_in, clk,resetn: in std_logic; d_ut: out std_logic_vector(7 downto 0) ); end srg8_right_maybe_wrong; architecture Behavioral of srg8_right_maybe_wrong is signal d_reg: std_logic_vector(7 downto 0); process (clk,resetn) if resetn='0' then d_reg<="00000000"; elsif clk'event and clk='' then d_reg(0)<= d_in; d_reg(7 downto ) <= d_reg(6 downto 0); d_ut<= d_reg; end Behavioral; Lab - - pls
library IEEE; use IEEE.STD_LOGIC_6.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity clock_divider is port(clk_in,resetn: in std_logic; en_div_clk: out std_logic); end clock_divider; architecture rtl of clock_divider is signal cnt_b: std_logic_vector(9 downto 0); process(clk_in,resetn) if resetn='0' then cnt_b<= "00000000000000000000"; elsif clk_in='' and clk_in'event then cnt_b<= cnt_b +; if cnt_b="" then en_div_clk<=''; else en_div_clk<='0'; end rtl; library IEEE; use IEEE.STD_LOGIC_6.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity cnt_bcd is port( clk, resetn, cnt_en: in std_logic; cnt: out std_logic_vector(3 downto 0)); end cnt_bcd; architecture rtl of cnt_bcd is signal cnt_b: std_logic_vector(3 downto 0); process(clk,resetn) if resetn='0' then cnt_b<= "0000"; elsif clk='' and clk'event then if cnt_en='' then if cnt_b="00" then cnt_b<= "0000"; else cnt_b<= cnt_b +; cnt<= cnt_b; end rtl; Lab - - pls