Geïntegreerd Practicum

Laurent Segers - laurent.segers@vub.be

Dugagjin Lashi - dugagjin.lashi@vub.be

Quentin Qeuvy - quentin.qeuvy@vub.be

Het Geïntegreerd Practicum omvat een aantal begeleide labo's waarin de studenten worden geconfronteerd met diverse elektronische componenten en schakelingen van gevorderd niveau, aangevuld met een pakket zelfstandig werk. De bedoeling is om verschillende bestaande elektronische componenten in een groter project in te bouwen. Een belangrijk aspect hierbij is het kunnen ontwerpen en implementeren van diverse schakelingen en software, waarbij debug-technieken en -tools gebruikt worden. Tijdens de labo's zullen we een remote LED-controller bouwen op basis van PIC-microcontrollers, FPGA's en draadloze modules. Dit project mag eenvoudig lijken, maar de samenhang van de verschillende onderdelen moet ervoor kunnen zorgen dat de eindgebruiker er maximaal comfort van kan hebben. Om dit tot een goed einde te brengen zullen er tijdens de labo's een aantal onderwerpen aan bod komen:

  • ontwerpen van elektronisch systemen op basis van bestaande technologiën,
  • gebruik kunnen maken van ingebedde microcontrollers en de mogelijke functionaliteiten,
  • het gebruik van FPGA's,
  • debugtechnieken voor ingebedde software,
  • debugtechnieken voor VHDL code (FGPA),
  • debuggen van diverse protocollen teneinde een goede communicatie tussen de verschillende ingebedde controllers/FPGA te kunnen garanderen,
  • project management,
  • het van gebruik sensoren en actuatoren, en
  • draadloze communicatietechnologie en draadloze aansturingen,
  • het kunnen tekenen van PCB's in Altium Designer.

Beoordeling

Tijdens het laatste labo zullen de studenten in groepen van 2 hun project presenteren. De presentatie omvat:

  • een korte uiteenzetting (5 min.) van de topologie van het ingebed systeem (FPGA, microcontroller, hardware, enz.) tesamen met de werking ervan,
  • een overtuigende demo (5 min.) van het systeem waarbij alle functionaliteiten worden gedemonstreerd,
  • vragenronde (5 min.) waarbij de assistenten en collega-studenten vragen kunnen stellen.

Remote LED controller driver

Als we aan een remote LED controller driver denken, is de kans groot dat we aan een LEDstrip controller denken die aangestuurd wordt met een kleine afstandsbediening. Hoewel dit over het algemeen het geval is, kan een remote LED-controller ook andere toepassingen vinden:

  • LED lichtkrant.
  • slimme verlichting die overdag de kleuren van LED's aanpast. Hiermee wordt dan zoveel mogelijk het natuurlijke bioritme gevolgd (zie ook F.Lux).
  • LED cubes
  • ambilight voor computerschermen
  • dynamische richtingsaanwijzers voor auto's, fietsers, enz. Hierbij kunnen de LED's afzonderlijk aangestuurd worden via o.a. I2C of one-wire
  • dynamische verkeersborden
  • vermogen elektronica die toelaat om 4 verschillende RGB-LEDstrips aan te sturen aan elk 2A. Dit is het equivalent van 600 RGB LED's!

In totaal vinden er 8 labo's plaats waarin verschillende onderdelen van het project aangereikt worden. Elke student krijgt dan ook de kans om zelf op de leerstof te kunnen experimenteren om een zo grondig inzicht te verwerven. De uren voorzien als zelfstandig werk worden aan het project besteed zodat elke groep een werkend protpype heeft. Merk hierbij op de aangereikte leerstof soms niet de meest optimale oplossing is voor een probleem. De methoden zijn generiek en kunnen in een groot aantal projecten gebruikt worden. Elke groep is vrij om een onderwerp te kiezen zolang er FPGA's en PIC-microcontrollers in het ontwerp gebruikt worden en het geheel op een finale PCB wordt afgeleverd. In de labo's worden een aantal generieke bouwstenen aangeleverd, maar de student is vrij om andere elementen te gebruiken zolang deze gelijkwaardig zijn aan de voorgestelde microcontrollers/FPGA's. Om de installatie van de nodige tools te vergemakkelijken, wordt een Linux virtuele machine aangeboden waarop zowel Xilinx ISE als gcc/g++ erop geïnstalleerd staan. Deze virtuele machine draait onder VMware player 12.0.

Resources

Tijdens de labo's worden er een aantal resources aangeboden die het tekenen in Altium zullen vergemakkelijken. Hieronder worden de demobordjes en shields ter download aangeboden zodat de student deze niet zelf meer hoeft te tekenen.

Labo 1

De FPGA

In de meeste toepassingen die we kunnen tegenkomen zullen we microcontrollers als het hart van het systeem kunnen aantreffen. Een microcontroller kan hierbij gezien worden als een miniatuur computer waarbij één of meerdere processorkernen de taken op een sequentiële wijze uitvoeren. Interruptroutines geven ons hierbij de illusie dat de processor verschillende taken tegelijk zou kunnen doen, maar een processor voert de taken (instructies) enkel sequentieel uit. Dankzij ingebouwde modules (UART, I2C, SPI, PWM, enz.) kan de processor met de buitenwereld communiceren.

Een FPGA daarentegen bevat standaard geen processor die instructies uitvoert. Een FPGA bevat digitale logica die door de gebruiker geconfigureerd kan worden om combinatorische schakelingen en/of finite state machines (FSM) te implementeren. Waardat bij een microcontroller alles sequentieel uitgevoerd wordt, wordt op een FPGA alle logica in parallel uigevoerd. Men moet dus in termen van hardware denken i.p.v. software.

Op een FPGA wordt logica getriggered op een welgegeven tijdsinterval. Dit laat toe om o.a. FSM's te laten evolueren in de tijd. Dit triggeren wordt gedaan a.d.h.v. een klok. De klok heeft een bepaalde frequentie en wordt gegeven door het kristal dat zich naast de FPGA op het bordje bevindt. Doorgaans wordt dit door de fabrikant van het bordje opgegeven. In de komende labo's zullen de Mojo-borden van Embedded Micro gebruikt worden. De FGPA's (Spartan 6) draaien hierbij aan een frequentie van 50MHz.

Het Mojo-bordje beschikt verder over 8 LED's en over een reeks IO-poorten. In wat volgt zullen eenvoudige voorbeelden gedemonstreerd worden om een counter, een kleine PWM-module en het aansturen van een 7-segementen display aan te sturen.

De taal waarin we deze modules zullen schrijven is VHDL (Very High Speed Integrated Hardware Description Language) en de implementatie zal gebeuren a.d.h.v. de Xilinx ISE tools. Het geheel kan gevonden worden in de virtuele machine die voor dit vak aangemaakt is.

Het eerste voorbeeld dat hier aangehaald zal worden is het aanmaken van een eenvoudige counter die op de LED's weergegeven zal worden. De 8 aanwezige LED's zorgen ervoor dat men een counter van 8 bit dient aan te maken zodat men 256 verschillende waarden kan weergeven. De LED's tonen in oplopende wijze de verschillende getallen (van 0 t.e.m. 256). In onderstaand codefragment wordt getoond hoe men dit implementeert.

LED_counter:process(clk)
	variable counter:unsigned(7 downto 0):=(others=>'0');
begin
	if (rising_edge(clk)) then
		counter:=counter+1;
		LEDs<=STD_LOGIC_VECTOR(counter);
	end if;
end process LED_counter;

Bovenstaade code bevat een aantal belangrijke elementen:

  • De input clock (clk): dit is het signaal waarop het process LED_counter zal triggeren. Bij elke trigger wordt het proces als het ware opgeroepen worden en wordt de hele code van het proces uitgevoerd.
  • Een klok bevat doorgaans 2 staten: high of low. Bij elke periode worden dus 2 overgangen gemaakt waardoor het proces 2 keren per periode opgeroepen zal worden. Om dit te vermijden wordt telkens getriggered op de stijgende flank van de klok. Dit wordt aangeduid als rising_edge. De tegenvariant, falling_edge bestaat ook.
  • Binnen het proces worden variabelen aangemaakt. Merk op dat een veriabele in VHDL iets helemaal anders betekent dan in een gewone programmeertaal. Een variabele is in feite een buffertje waarin een aantal bits (vector) worden bijgehouden. Het aantal bits kan vrij gekozen worden. Dus hier kan men een variabele aanmaken van 17 bits. In een gewone programmeertaal zoals C, C++, enz. is het echter enkel mogelijk om variabelen te alloceren die een veelvoud van 8 bits hebben.
  • VHDL is een sterk getypeerde taal. Dit betekent dus dat alle variabelen (en signalen) van een datatype zijn. Dit is nog niet duidelijk in het huidige voorbeeld, maar komt later zeker terug.

Het voorbeeld van hierboven laat toe om tussen 0 en 255 te tellen. Wat het voorbeeld echter niet toont is hoe men met deze counter de waarde op de LED's zal kunnen weergeven. Om dit te kunnen doen moeten we de counter (het proces) embedden in een VHDL module. Dit wordt in onderstaande codefragment gedaan.

----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
----------------------------------------------------
entity LEDcounter is
	port(
		clk: 		IN 		STD_LOGIC;
		LEDs:		OUT		STD_LOGIC_VECTOR(7 downto 0)
	);
end LEDcounter;
----------------------------------------------------
architecture Behavioral of LEDcounter is
----------------------------------------------------
begin
	------------------------------------------------
	LED_counter:process(clk)
		variable counter:unsigned(7 downto 0):=(others=>'0');
	begin
		if (rising_edge(clk)) then
			counter:=counter+1;
			LEDs<=STD_LOGIC_VECTOR(counter);
		end if;
	end process LED_counter;
----------------------------------------------------
end Behavioral;
----------------------------------------------------

Poorten en gedrag

Elke VHDL code vertrekt vanuit een aantal voorafbepaalde datatypen en operatoren. Deze worden in bestaande bibliotheken meegeleverd en kunnen door de VHDL-codeur gebruikt worden. Bibliotheken gebruiken gebeurt door het woordje "use" gevolgd door de naam van de bibliotheek. Over het algmeen geldt dat de bibliotheken zoals hierboven getoond voldoende zijn voor veel basisprojecten.

Een VHDL-module bestaat uit 2 grote delen: de beschrijving van de input-output (poorten) en de beschrijving van het gedrag van de module (de behavioral).

De poorten zijn de letterlijke verbindingen van de module met de buitenwereld. De declaratie ervan is altijd als volgt:

  • de naam van de poort gevolgd door een dubbele punt,
  • of het een input, output of beide is,
  • hoeveel bits de poort in beslag neemt (STD_LOGIC) of (STD_LOGIC_VECTOR).

Bij het declareren van poorten wordt per conventie steeds met STD_LOGIC of met STD_LOGIC_VECTOR gewerkt. Dit maakt de potentiële communicatie met andere modules eenvoudiger omdat er dan tussen de modules geen lastige typeconversies moet plaatsvinden.

Naast de beschrijving van de poorten wordt ook het gedrag van de module geschreven. In een vorig codefragment is het gewenste gedrag reeds beschreven en dit kan rechtreeks hierin geïmplementeerd worden. Merk op dat klok van het gedrag overeenkomt met de klokbeschrijving van in de poorten. Ook het "LEDs"-signaal is reeds bij de poorten beschreven. Het aantal bits (en type) van de poorten en de aanspreking ervan in de code moet exact overeenkomen voor het gewenste resultaat.

Signalen vs. variables

In VHDL worden regelmatig signalen en variables door elkaar gebruikt. Beide lijken hetzelfde te doen, maar hun gebruik is totaal verschillend. Variabelen worden enkel lokaal in een proces gebruikt. Het toewijzen van een waarden aan een variabele gebeurt door het ":=" teken. Een variabele wordt in digitale logica vertaald naar een latch. Dit betekent dus dat een variabele onmiddelijk van waarde verandert na de toewijzing. Een signaal daarentegen wordt over het algemeen vertaald naar een flipflop. Dit betekent dus dat een signaal zijn toewijzing pas ziet na de volgende (klok)cyclus. Tot deze cyclus blijft een signaal zijn oude waarde behouden. Hierdoor wordt een signaal gebruikt als communicatielijn tussen verschillende modules. De syntax van toewijzing is dan ook verschillend dan die van een variabele, nl. "<=".

In VHDL is het ook mogelijk om commentaar te schrijven. Dit wordt gedaan door de commentaar door 2 "-" te laten voorafgaan. Dit symbool heeft enkel effect op de daaropvolgende tekst tot het einde van de huidige regel.

Verbinding met de fysieke IO-poorten van de FPGA

Hoewel we een afgewerkte module hebben geschreven, is het nog niet mogelijk om de code op een FPGA te implementeren. Een belangrijke stap is mappen van de poorten van de module naar de fysieke pinnen van de FPGA. Dit kunnen we aan de tools duidelijk maken door gebruik te maken van een user constraints file (UCF). Dit wordt hieronder voorgedaan.

net 	"clk"		LOC=P56;
net 	"LEDs<0>"	LOC=P134;
net 	"LEDs<1>"	LOC=P133;
net 	"LEDs<2>"	LOC=P132;
net 	"LEDs<3>"	LOC=P131;
net 	"LEDs<4>"	LOC=P127;
net 	"LEDs<5>"	LOC=P126;
net 	"LEDs<6>"	LOC=P124;
net 	"LEDs<7>"	LOC=P123;

Het geheel (VHDL + UCF) synthetiseren en compileren naar een *.bin bestand levert de "firmware" op die op de FPGA gedownload kan worden.

Klokdeler

De LED counter die we hiernet hebben geschreven zal op de LED's van de FPGA weldegelijk het gewenste resultaat opleveren. Het enige lastige eraan is dat de LED's zodanig snel van toestand zullen wisselen dat het menselijke oog het tellen niet zal kunnen waarnemen. Om dit toch nog te kunnen zien zal men de frequentie van het tellen moeten verlagen. Dit kan men doen door de klokfrequentie te delen. Om het comfortabel te maken zullen we de frequentie van het tellen verlagen naar 1Hz. Hierdoor moet men de originele klok delen door 50 miljoen. Men moet dus 50 miljoen klokticks wachten vooraleer men het getal met 1 eenheid mag ophogen. Dit kan men doen a.d.h.v. een nieuwe teller.

Zoals reeds vermeld kunnen we in VHDL zelf variabelen definiëren van een bepaalde bitlengte (vector). Om een vector aan te maken waarin we minimaal 50 miljoen waarden in kunnen bijhouden, moeten we een vector aanmaken van 26 bits (67 miljoen waarden). Het volstaat dan om deze vector bij elke kloktick te laten ophogen en te vergelijken of er 50 miljoen ticks verstreken zijn (zie onderstaand codefragment).

LED_counter:process(clk)
	variable counter:unsigned(7 downto 0):=(others=>'0');
	variable clk_counter:unsigned(25 downto 0):=(others=>'0');
begin
	if (rising_edge(clk)) then
		clk_counter:=clk_counter+1;
		if (clk_counter=50000000) then -- compare if equal to 50*10^6
			counter:=counter+1;
			LEDs<=STD_LOGIC_VECTOR(counter);
			clk_counter:=(others=>'0'); -- reset counter
		end if;
	end if;
end process LED_counter;

Nieuw hierbij is dat men een variabele kan vergelijken met een voorafbepaalde waarde. Algemeen geldt dat men een variabele kan vergelijken met een waarde die van hetzelfde datatype is. Een vergelijking vindt steeds plaats met het symbool "=".

Constanten

VHDL laat toe om naast variabelen en signalen ook constanten te gebruiken. De constanten hebben in VHDL hetzelfde effect als in andere programmeertalen. Constanten worden niet mee in de finale structuur van de FPGA opgenomen, maar de waarde van de constante wordt overal gesubstitueerd. Bijvevolg worden vaak de natural (geheel positief getal) en de integer (geheel positief en negatief getal) als datatype gebruikt. Een voorbeeld van een constante wordt in onderstaand codefragment weergegeven.

constant clk_ticks:NATURAL:=50000000;

Een constante wordt steeds onmiddelijk toegewezen met zijn finale waarde. Dit wordt gedaan a.d.h.v. ":=". Constantes worden typisch in de behavioral zelf geplaatst zodat deze zichtbaar zijn doorheen heel de VHDL module. Het declareren en gebruiken van de net aangehaalde constante in de LED counter wordt in het volgende codefragment duidelijk gemaakt.

----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
----------------------------------------------------
entity LEDcounter is
	port(
		clk: 		IN 		STD_LOGIC;
		LEDs:		OUT		STD_LOGIC_VECTOR(7 downto 0)
	);
end LEDcounter;
----------------------------------------------------
architecture Behavioral of LEDcounter is
	-- declare constant values here
	constant clk_ticks:NATURAL:=50000000;
----------------------------------------------------
begin
	------------------------------------------------
	LED_counter:process(clk)
		variable counter:unsigned(7 downto 0):=(others=>'0');
		variable clk_counter:unsigned(25 downto 0):=(others=>'0');
	begin
		if (rising_edge(clk)) then
			clk_counter:=clk_counter+1;
			if (clk_counter=clk_ticks) then -- compare if equal to 50*10^6
				counter:=counter+1;
				LEDs<=STD_LOGIC_VECTOR(counter);
				clk_counter:=(others=>'0'); -- reset counter
			end if;
		end if;
	end process LED_counter;
----------------------------------------------------
end Behavioral;
----------------------------------------------------

Opdrachten

  • Schrijf een PWM module die een refreshrate van 200kHz heeft en een waarde tussen 0 en 256 als input kan aannemen. De input zijn 8 IO-pinnen die je met breadboardkabels hoog/laag trekt.
  • Een veel voorkomende module is een klokdeler (Eng. clock divider). Schrijf een klokdeler waarbij je een constante waarde opneemt die je toelaat om de verhoudingsfactor te bepalen. Een klokdeler heeft 1 input (de FPGA-klok) en 1 uitgang (de trage klok). Ga dit na door een klokdeler te maken die als uitgang een signaal aan respectievelijk 1Hz, 2Hz en 5Hz. De variabele die je gebruikt om het aantal FPGA-klok tiks bij te houden mag je voorlopig 32 bits breed nemen.
  • In deze opgave ga je een 7-segementen display aansturen. Laat de gebruiker toe om via 4 IO-pinnen een getal tussen 0 en 15 te kiezen (0 en F). Zorg ervoor dat de display op een correcte manier aangestuurd wordt. Maak gebruik van een switch case om te selecteren tussen de verschillende getallen.

Labo 2

VHDL schrijven kan snel een complexe aangelegenheid worden. In VHDL is het mogelijk om heel grote designs te ontwerpen, waarbij verschillende modules aan elkaar gekoppeld worden om een groot geheel te vormen. Net zoals software moet VHDL gevalideerd kunnen worden op correctheid van werking. Een eerste lijn van controle is de syntax-check. Deze laat toe om na te gaan of de geschreven tekst weldegelijk voldoet aan de VHDL standaard. Het is echter zo dat deze syntax-check niet gerandeerd ofdat een module effectief ook werkt, integendeel! Om de correctheid van de code na te gaan zal men de VHDL code eerst debuggen a.d.h.v. testbenches. Tenstbenches bestaan uit waveforms die toelaten om via stimuli (aangelegde signalen) de evolutie tussen ingang en uitgang van een module waar te nemen. Een belangrijk concept is hierbij dat men de relatie tussen ingangen en uitgangen goed in het oog dient te houden, i.h.b. de exacte timing tussen beide. om het gebruik van testbenches aan te tonen zullen we eerst een nieuwe module aanmaken die het mogelijk maakt om data tussen de FPGA en een microcontroller of computer uit te wisselen: de Universal Asynchronous, Receive and Transmission (UART) module. Hierbij starten we eerst met het transmit-gedeelte (TX) van UART.

UART TX

UART is een point-to-point communicatieprotocol met 2 signaallijnen (TX en RX, full duplex). Dit laatste maakt de implementatie van UART eenvoudig aangezien men 1 pin voor het transmit- en 1 voor het receive-gedeelte kan voorbestemmen. De UART-communicatie gebeurt als volgt:

  • Wanneer er geen data verstuurd wordt, is de datalijn passief hoog. Men laat de lijn zweven en een pull-up weerstand doet hier zijn werk.
  • Bij het versturen van data, wordt er eerst een startbit berstuurd. Deze startbit is altijd actief laag (logisch 0).
  • Na de startbit volgen de 7,8 of 9 databits. De datalijn kan dus hoog of laag zijn.
  • Als laatste volgt de stopsequentie. Deze bestaat doorgaans uit 1, 1.5 of 2 stopbits. Een stopbit is actief hoog (logisch 1).
  • Na de stopsequentie wordt de lijn weer passief hoog gelaten.

Heel de operatie kan samengevat worden in een toestandsmachine (FSM). De FSM van de UART-module zou er als volgt kunnen uitzien:

  • Blijf in IDLE zolang dat er geen data (send_data = 0) verstuurd moet worden.
  • Ga over naar de STARTBIT indien send_data = 1.
  • Na de startbit wordt overgegaan naar SENDDATA. Blijf in SENDDATA zolang dat niet alle databits verzonden zijn.
  • Zijn alle databits verzonden, ga dan over naar de STOPBITS.
  • Als laatste mag men terugkeren naar de IDLE toestand, waarop men opnieuw wacht op een nieuwe te verzenden byte.

De beschreven toestanden zijn dus: IDLE, STARTBIT, SENDDATA en STOPBIT. De UART-module (enkel TX) in VHDL kan in onderstaand codefragment gevonden worden. Merk op dat we de baudrate op 9600 hebben gezet.

----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
----------------------------------------------------
entity UART is
	port(
		clk: 		IN 		STD_LOGIC;
		TX:			OUT		STD_LOGIC;
		TX_enable:	IN		STD_LOGIC;
		TX_data:	IN		STD_LOGIC_VECTOR(7 downto 0)
	);
end UART;
----------------------------------------------------
architecture Behavioral of UART is
	begin
	----------------------------------------------------
	UART_TX:process(clk)
		-- declare a type for our statemachine
		type UART_TX_STATE is (IDLE,STARTBIT,SENDDATA, STOPBIT);
		variable state:UART_TX_STATE:=IDLE;
		variable data:STD_LOGIC_VECTOR(7 downto 0);
		variable databit_counter:unsigned(3 downto 0); -- count the amount of bits sent + 1
		variable clk_divider:unsigned(13 downto 0):=(others=>'0'); -- count up to 5208 ticks for 9600 baud per second
	begin
		if (rising_edge(clk)) then
			clk_divider:=clk_divider+1;
			if (clk_divider=5208) then
				clk_divider:=(others=>'0');
				case state is
					when IDLE =>
						TX<='1';
						databit_counter:=(others=>'0');
						if (TX_enable='1') then
							state:=STARTBIT;
							data:=TX_data;
						end if;
					when STARTBIT =>
						TX<='0';
						state:=SENDDATA;
					when SENDDATA =>
						TX<=data(to_integer(databit_counter));
						databit_counter:=databit_counter+1;
						if (databit_counter=8) then
							state:=STOPBIT;
						end if;
					when STOPBIT =>
						TX<='1';
						if (TX_enable='0') then
							state:=IDLE;
						end if;
				end case;
			end if;
		end if;
	end process UART_TX;
----------------------------------------------------
end Behavioral;
----------------------------------------------------

Voor de eenvoud zijn we hier uitgegaan van 1 start- en stopbit en 8 databits. De module kan met een logic state analyser (digitale oscilloscoop) bemonsterd worden om te zien of het gewenste effect bereikt is. Het is echter gemakkelijker om de module eerst in een testbench uit te testen. Het uittesten van een module gebeurt in een aantal stappen:

  • Eerst wordt een lege testbench aangemaakt waarbij er gerefeerd wordt naar de module die te testen is.
  • Eenmaal het bestand is aangemaakt worden de nodige signalen aangelegd zodat de ingangen en uitgangen in de waveforms gekoppeld kunnen worden.
  • Als laatste worden de klok en eventuele stimuli aangelegd. De testbench voor de UART-TX module wordt hieronder weergegeven. Merk op dat de code een snelheid heeft van 50MHz i.p.v. de gebruikelijke 100MHz die door Xilinx gehanteerd wordt.
--------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

ENTITY UART_tb IS
END UART_tb;

ARCHITECTURE behavior OF UART_tb IS

	-- Component Declaration for the Unit Under Test (UUT)

	COMPONENT UART
	PORT(
		clk : IN  std_logic;
		TX : OUT  std_logic;
		TX_enable : IN  std_logic;
		TX_data : IN  std_logic_vector(7 downto 0)
		);
	END COMPONENT;

	--Inputs
	signal clk : std_logic := '0';
	signal TX_enable : std_logic := '0';
	signal TX_data : std_logic_vector(7 downto 0) := (others => '0');
	--Outputs
	signal TX : std_logic;
	-- Clock period definitions
	constant clk_period : time := 20 ns;

	BEGIN
	-- Instantiate the Unit Under Test (UUT)
	uut: UART PORT MAP (
		clk => clk,
		TX => TX,
		TX_enable => TX_enable,
		TX_data => TX_data
		);
	-- Clock process definitions
	clk_process :process
	begin
		clk <= '0';
		wait for clk_period/2;
		clk <= '1';
		wait for clk_period/2;
	end process;


	-- Stimulus process
	stim_proc: process
	begin
		-- hold reset state for at least 100 ns.
		TX_enable<='0';
		wait for 1 ms;
		TX_enable<='1';
		TX_data<="00110010";
		wait for clk_period*10000;
		TX_enable<='0';
		wait;
	end process;
END;

Als finale test kan er data tussen een computer en de FPGA uitgewisseld worden. Met een klein C-programma kan dit bereikt worden.

Opdrachten

  • Naast het TX-gedeelte beschikt de UART ook over een RX-gedeelte. Implementeer deze ook voor een baudrate van 9600. Pro-tip: tracht elke bit in het midden van duurtijd te bemonsteren. Daar is de kans zeer klein dat men verkeerdelijk een andere bit zou inlezen.

Labo 3

In dit labo zullen we uitgebreider ingaan op de basisconcepten van VHDL. Vaak is het zo dat een VHDL-implementatie niet bestaat uit een enkele functionaliteit, maar uit verschillende elementen die samen een geheel vormen. VHDL laat hierbij toe om verschillende functionaliteiten apart te coderen en nadien samen te voegen. Deze stappen verschillen niet heel veel t.o.v. het coderen van een enkele module. Echter zullen we vanaf nu in verschillende lagen modules aan elkaar koppelen, ttz. in een hiërarchie. Om dit te kunnen doen schrijven we eerst afzonderlijke VHDL modules, waarna we ze zullen doorkoppelen in een toplevel. Als voorbeeld zullen we een eenvoudig RGB-PWM module maken die zelf uit 3 afzonderlijke PWM-modules bestaat. De interne werking van een PWM-module wordt hierbij aan de lezer overgelaten. Een PWM-module kan onderstaande poortdefinitie hebben.

entity PWM is
	port(
		clk: 		IN 		STD_LOGIC;
		value:		IN		STD_LOGIC_VECTOR(7 downto 0);
		PWM_out:	OUT		STD_LOGIC;
		enable:		IN		STD_LOGIC
	);
end PWM;

Deze module kan zelf geïmplementeerd worden op de FPGA door de poorten in deze definitie te koppelen aan de fysieke pinnen van de FPGA (via de UCF-file). Hierdoor wordt deze module de hoogste module in de hiërarchie en kan men dus met de FPGA niets anders meer doen. Om een RGB-module te kunnen maken moeten we beschikken over 3 PWM modules, dus een per kanaal. Deze 3 modules zullen we dus in parallel schakelen in een hoger niveau. Dit hoger niveau heet dan ook een toplevel. Het toevoegen van een module in een toplevel gebeurt in 3 fasen:

  • declaratie van de betrokken module in de toplevel,
  • instantiatie van de betrokken module als een bruikbaar element en,
  • het voorzien van de nodige "bekabeling" om van de module naar de rest van de toplevel en of buitenpoorten te kunnen communiceren.

Het geheel wordt gedemonstreerd in onderstaand codefragment.

----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
----------------------------------------------------
entity RGB_MODULE is
	port(
		clk: 		IN 		STD_LOGIC;
		channel_r:	OUT		STD_LOGIC;
		channel_g:	OUT		STD_LOGIC;
		channel_b:	OUT		STD_LOGIC;
		en:		IN		STD_LOGIC
	);
end RGB_MODULE;
----------------------------------------------------
architecture Behavioral of RGB_MODULE is
	-- declare our PWM module here. Please note that we replaced
	-- entity (from port declaration) with component
	component PWM is
	port(
		clk: 		IN 		STD_LOGIC;
		value:		IN		STD_LOGIC_VECTOR(7 downto 0);
		PWM_out:	OUT		STD_LOGIC;
		enable:		IN		STD_LOGIC
	);
	end component;
	-- if you have other modules to declare, put them
	-- also here before the begin keyword
	component RGB_master is
	port(
		clk:			IN		STD_LOGIC;
		R:			OUT		STD_LOGIC_VECTOR(7 downto 0);
		G:			OUT		STD_LOGIC_VECTOR(7 downto 0);
		B:			OUT		STD_LOGIC_VECTOR(7 downto 0)
	);
	end component;
----------------------------------------------------
-- we need to declare the signals which we will use
-- in order to interconnect all the modules properly
-- note: only signals which do not belong to the port
-- definition of the toplevel must be declared here
	signal PWM_value_r, PWM_value_g, PWM_value_b: STD_LOGIC_VECTOR(7 downto 0);
----------------------------------------------------
begin
	-- instantiate the modules by giving them a name
	-- all names shoud be unique.
	RGB_R:PWM
	port map(
		clk=>clk,
		value=>PWM_value_r,
		PWM_out=>channel_r,
		enable=>en
	);
	------------------------------------------------
	-- we declare the same module with a different
	-- name and different nets
	RGB_G:PWM
	port map(
		clk=>clk,
		value=>PWM_value_g,
		PWM_out=>channel_g,
		enable=>en
	);
	------------------------------------------------
	RGB_B:PWM
	port map(
		clk=>clk,
		value=>PWM_value_b,
		PWM_out=>channel_b,
		enable=>en
	);
	------------------------------------------------
	RGB_controller: RGB_master
	port map(
		clk=>clk,
		R=>PWM_value_r,
		G=>PWM_value_g,
		B=>PWM_value_b
	);
----------------------------------------------------
end Behavioral;
----------------------------------------------------

In bovenstaande code valt op dat we een extra module hebben toegevoegd die de waarde van de PWM-kanalen aangeeft. Deze module kan naar hartelust ingevuld worden om het gewenste resultaat te bereiken. Deze module kan bijgevolg dus ook aangepast worden om een waarde vanuit een remote locatie aan te nemen en door te geven naar het gepaste PWM-kanaal.

Bij het uitschrijven van een toplevel is het belangrijk om 2 type signalen te onderscheiden: de signalen die intern in de module zelf blijven en de signalen die rechtstreeks aan de input/output van de toplevel gekoppeld zijn. Signalen die aan de IO van de toplevel gekoppeld zijn mogen in geen geval opnieuw gedeclareerd worden als signalen binnen de toplevel. Deze zorgen er immers voor dat de interne onderdelen met de buitenwereld klappen. De poortdefinitie van een module/toplevel declareert deze signalen dus al. De andere (interne) signalen moeten wel aangemaakt worden zodat de interne modules gekoppeld kunnen worden. Voor de interne signalen geldt dat er steeds maximaal 1 aansturende module is. Het aantal lezende modules is voor elk signaal onbeperkt. Met een aansturende module wordt bedoeld dat deze module het signaal actief hoog en laag trekt. Moesten 2 modules eenzelfde signaal willen aansturen zou men immers kortsluiting kunnen veroorzaken. Bij de signalen die extern gekoppeld worden geldt dat men een output signaal door 1 enkele interne module mag aansturen. Een inkomend signaal mag door alle modules geraadpleegd worden.

UCF

De pinmapping naar de fysieke FPGA gebeurt door enkel de poorten van de toplevel in de UCF-file op te nemen. Alle andere poorten van de submodules opnemen in de UCF-file zal tot fouten leiden en zal ervoor zorgen dat de VHDL-code niet te implementeren zal zijn. In ISE kan men een module als toplevel aanduiden er erop te rechterklikken en te selecteren als topmodule.

Generics

VHDL-code laat toe om het gedrag vaan hardware te gaan bepalen. Het is echter ook mogelijk modules aan te maken waarbij een aantal parameters nadien ingevuld kunnen worden. Met parameters bedoelen we o.a. het aantal nodige bits om signaal aan te maken, tot hoeveel een teller moet tellen om de gepaste klokdeler te te kunnen maken, enz. Dit soort van parameters worden tijdens het coderen generiek ingevuld en worden bijgevolg ook generics genoemd. Het gebruik van generics wordt in onderstaand codefragment van een klokdeler duidelijk gemaakt.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
----------------------------------------------------
entity CLKDivider is
	generic
	(
		scalefactor:NATURAL:=16
	);
	port
	(
		clk_in:   IN 		STD_LOGIC;
		clk_out:    OUT		STD_LOGIC
	);
end RGB_MODULE;
----------------------------------------------------
architecture Behavioral of RGB_MODULE is
begin
	clk_div_process:process(clk)
		variable counter:UNSIGNED(31 downto 0):=(others=>'0'); -- take 32 bits for now.
		variable clk_temp:STD_LOGIC:='0';
	begin
		if (rising_edge(clk)) then
			if (counter=scalefactor/2) then -- we want to divide by 2 for both rising and falling edge
				clk_temp:=not clk_temp; -- invert the signal
				counter:=(others=>'0');
			else
				counter:=counter+1;
			end if;
			clk_out<=clk_temp;
		end if;
	end proces clk_div_process;
end Behavioral;

De generic scalefactor laat in dit voorbeeld toe om de originele klok te delen door een willekeurig getal dat later in de toplevel opgegeven zal worden. De definitieregels van de generics zijn dezelfde als de gewone poortdefinities (punt-komma, datatype e.d.). Merk op dat generics enkel nodig zijn om parameters in te stellen. Deze zullen dus niet zelf gesynthetiseerd worden tot logica. Om die reden kan men voor generics gerust datatypen zoals natural en integer gebruiken. De waarde achter het datatype wordt doorgaans ook al aangeduid met een tijdelijke richtwaarde. Deze kan in de toplevel overschreven worden door een ander getal.

Het invullen van de generic in de toplevel verloopt op een redelijk eenvoudige werkwijze. Het volstaat om de module te definiëren, te instaniëren en de generics samen met de poorten in te vullen. Hieronder het invullen van de generic uit bovenstaande module aangetoond.

----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
----------------------------------------------------
entity toplevel is
	port
	(
		clk: 		IN 		STD_LOGIC;
		-- other ports here
	);
end toplevel;
----------------------------------------------------
architecture Behavioral of toplevel is
	-- declare our PWM module here. Please note that we replaced
	-- entity (from port declaration) with component
	component CLKDivider is
	generic
	(
		scalefactor:NATURAL:=16
	);
	port
	(
		clk_in:   IN 		STD_LOGIC;
		clk_out:    OUT		STD_LOGIC
	);
	end component;
----------------------------------------------------
	signal clk_slow:STD_LOGIC;
begin
----------------------------------------------------
	clk_div1:CLKDivider
	generic map
	(
		scalefactor=>100
	)
	port map
	(
		clk_in=>clk,
		clk_out=>clk_slow
	);
----------------------------------------------------
end Behavioral;
----------------------------------------------------

In bovenstaand voorbeeld wordt er een klokdeler aangemaakt die de orginele klok met een factor 100 vertraagt. Ook de in toplevel wordt eenzelfde syntax als voor de poorten gebruikt. Merk op dat er tussen de generic map en de port map geen punt-komma vereist is.

Tesbenches

Tesbenches kunnen ook uitgevoerd worden op multimodule systemen. het volstaat hierbij om de toplevel aan te duiden en te simuleren. In de simulatie is het mogelijk om de interne signalen van de modules weer te geven in de waveforms. Hierbij volstaat de optie "Add to wave window" te gebruiken.

Opgaven

  • Vervolledig the RGB-module van hierboven zodat een RGB-LED aangestuurd kan worden met een aantal voorafbepaalde kleurinstellingen. De instelligen mogen in de master-module geïmplementeerd worden.
  • Schrijf een multiplexer module die toelaat om via een selector te kiezen tussen 2 ingangssignalen. Afhankelijk van de waarde van de selector (1 of 0) wordt ofwel input1 ofwel input 2 gebracht aan de uitgang. Zorg ervoor dat de busbreedte van de ingangen en uitgang (met uitzondering van de selector die 1 bit is) willekeurig gekozen kan worden.
  • De huidige UART-module (RX+TX) van voorgaand labo laat toe om maar 1 enkele byte gelijktijdig uit te wisselen. Beter zou zijn om in VHDL een aantal bytes in een buffer op te nemen zodat men kortstondig een stream van bytes kan versturen zonder hiervoor onndige lociga in andere modules te implementeren. Schrijf een buffermechanisme die toelaat om 8 bytes te bufferen en byte per byte door te sturen naar de UART. De buffer maakt gebruik van het ringbuffer-mechanisme. De buffer kijkt steeds na of er effectief data verstuurd moet worden. Indien ja polst die de UART (TX) module om na te gaan of er een byte verstuurd kan worden. Indien ja wordt een byte aangeboden en wordt er gewacht totdat de UART-module in verzending-mode overgaat. Van zodra de UART-module klaar is kan overgegaan worden naar een eventuele volgende gebufferde byte. Test de modules hierbij ook apart vooraleer ze in een toplevel te implementeren.

Labo 4

VHDL laat toe om grote modules te schrijven die zelf nog eens onderverdeeld zijn in een aantal submodules. In wat volgt zullen een aantal technieken aan bod komen die toelaten om code verder op te schonen en om zelf eigen datatypes (structuren) te voorzien. Hierbij zullen we het gebruik van packages aantonen waarin zowel datastructuren als functies geschreven kunnen worden. Een package aanmaken vereist speciale syntax die heel gelijklopend is aan de syntax van een gewone module. De basissyntax van een package kan in onderstaande codefragment gevonden worden.

-- package UTILS.
-- In this package some usefull operations are defined. 
-- Author: Laurent Segers

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
--------------------------------------------------------------
package utils is
end utils;
--------------------------------------------------------------
package body utils is
end utils;
--------------------------------------------------------------

Merk herbij op dat het een goede praktijk is om de naam van de auteur van de package hierbij te vermelden. De functieprototypes komen in het gedeelte van de package zelf, tesamen met de declaratie van constanten en zelfgedefinieerde datatypes. In het gedeelte van de body komen dan de ingevulde functies.

packages hebben ook andere opties, maar deze worden hier even achterwege gelaten. Een veelvoorkomende tekortkoming binnen VHDL is het weten van het aantal signaallijnen in een bus om een gegeven maximale waarde te kunnen voorstellen. Tot hiertoe is men steeds verplicht geweest dit manueel uit te rekenen a.d.h.v. een log2-operatie. Men kan in VHDl zelf een functie schrijven die ons toelaat om een log2 uit te rekenen gebaseerd op de maximale waarde die we in een bus wensen. Een eenvoudige strategie is door gebruik te maken van een loop die de gewenste waarde als argument opneemt en steeds deelt door 2 totdat men het getal 0 uitkomt. Zolang we kunnen delen wordt er een teller bijgehouden die bij elke iteratie met 1 opgehoogd wordt. De functie en functieprototype worden hieronder toegevoegd aan de package.

-- package UTILS.
-- In this package some usefull operations are defined. 
-- Author: Laurent Segers

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
--------------------------------------------------------------
package utils is
	-- Function to calculate the log2 of a given natural value, the output is rounded to upper 
	-- arg1: the value to take the log of
	-- return: the log2 value of arg1 
	function log2(value:NATURAL) return NATURAL;
end utils;
--------------------------------------------------------------
package body utils is
	----------------------------------------------------------------------------
	function log2(value:NATURAL) return NATURAL is
		variable counter:NATURAL;	
		variable val:NATURAL;
	begin
		counter:=0;
		val:=value;
		while val>0 loop
			counter:=counter+1;
			val:=val/2;
		end loop;
		return counter;
	end log2;
	----------------------------------------------------------------------------
end utils;
--------------------------------------------------------------

In deze functie hebben we enkel variabelen van het type natural gebruikt. De functie log2 kan in VHDL gebruikt worden binnen het uitvoeren van een stukje code. De log2 wordt echter typisch gebruikt tijdens het synthetiseren en wordt dus dan eigenlijk niet rechtreeks vertaald naar effectieve logica. Merk op dat men uit een argument enkel kan lezen. het is dus niet toegelaten om in VHDL een waarde in de variabele van een argument toe te wijzen.

De functie log2 kan in het huidige project opgeroepen worden. Echter moet men in de module waarin men deze functie wenst te gebruiken eerst de package declareren zodat de functie zichtbaar wordt. Hieronder wordt een voorbeeld gegeven van het gebruik van de log2 functie. De naam van de package maakt uit en in het huidige voorbeeld is dit "utils.vhd".

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
-- include here our own library (package)
library work;
use work.utils.ALL;
----------------------------------------------------------------------------------
entity Clkdiv is
	generic(
		divider:NATURAL:=16
	);
	port(
		clk:		IN			STD_LOGIC;
		clkDiv:		OUT		STD_LOGIC
	);
end Clkdiv;
----------------------------------------------------------------------------------
architecture Behavioral of Clkdiv is
	-- use here the function log2 
	signal counter:UNSIGNED(log2(divider/2)-1 downto 0):=(others=>'0');
begin
	clkDivProc:process(clk)		
		variable outputDef:STD_LOGIC:='0';
	begin
		if (rising_edge(clk)) then
			if (counter=divider/2-1) then
				outputDef:=not outputDef;
				counter<=(others=>'0');
			else
				counter<=counter+1;
			end if;
			clkDiv<=outputDef;
		end if;
	end process clkDivProc;
end Behavioral;
----------------------------------------------------------------------------------

Het gebruik van een bibliotheek gebeurt hier in 2 fasen: eerst moet men de namespace opgeven (hier work), waaronder wij de package utils gebruiken. Het is mogelijk om datatypes en functies apart te includen, maar voor het gemak includen we hier de volledige package. Dit wordt aangeduid met de ".ALL". Nadien kan de functie log2 gebruikt worden zoals een functie in willekeurige programmeertaal. De synthesizer subsitueert het resultaat van de log2 functie op de plaats waar log2 staat.

Men kan dit principe verder doortrekken naar andere functies. Hieronder wordt het voorbeeld gegeven van een sign-extend functie. Deze functie neemt 2 argumenten op en retourneert de waarde in een vector terug die meer bits bevat dan het originele. Nieuwigheid aan deze functie is dat men a.d.h.v. de naam van een vector ook de lengte van de vector kan bepalen. Dit laatste wordt aangeduid met een 'length eigenschap. Het begrijpen van de functielogica wordt aan de lezer overgelaten.

-- package UTILS.
-- In this package some usefull operations are defined. 
-- Author: Laurent Segers

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
--------------------------------------------------------------
package utils is
	-- Function to calculate the log2 of a given natural value, the output is rounded to upper 
	-- arg1: the value to take the log of
	-- return: the log2 value of arg1 
	function log2(value:NATURAL) return NATURAL;
	-- Function to calculate the sign extended value
	-- arg1: original value
	-- arg2: total bus widht of returned std_logic_vector
	-- return: the sign extended std_logic_vector
	function signextend(value:STD_LOGIC_VECTOR;extend_length:NATURAL) return STD_LOGIC_VECTOR;
	-- arguments are always separated by a ′;′
end utils;
--------------------------------------------------------------
package body utils is
	----------------------------------------------------------------------------
	function log2(value:NATURAL) return NATURAL is
		variable counter:NATURAL;	
		variable val:NATURAL;
	begin
		counter:=0;
		val:=value;
		while val>0 loop
			counter:=counter+1;
			val:=val/2;
		end loop;
		return counter;
	end log2;
	----------------------------------------------------------------------------
	function signextend(value:STD_LOGIC_VECTOR;extend_length:NATURAL) return STD_LOGIC_VECTOR is
		variable sign_bit:std_logic;
		variable return_vector:std_logic_vector(extend_length-1 downto 0);
	begin
		sign_bit:=value(value′length-1);
		return_vector(extend_length-1 downto value′length):=(others=>sign_bit);
		return_vector(value′length-1 downto 0):=value;
		return return_vector;
	end signextend;
	----------------------------------------------------------------------------
end utils;
--------------------------------------------------------------

Naast het schrijven van functies kan men in packages ook constanten en datatypes declararen. Het aanmaken hiervan in een package kan handig zijn indien men verschillende modules met elkaar in een grotere module wilt laten samenwerken. Het declararen wordt hieronder weergegen. Merk op dat men constanten en datatypes enkel in de package zelf schrijft en dus niet in de body van een package.

-- package UTILS.
-- In this package some usefull operations are defined. 
-- Author: Laurent Segers

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
--------------------------------------------------------------
package utils is
	-- declare a constant for the buswidth between modules
	CONSTANT BUSWIDTH:NATURAL:=32;
	-- declare a (sub)type to have a fixed datastructure between modules
	subtype BUS_st is STD_LOGIC_VECTOR(BUSWIDTH-1 downto 0);

	-- Function to calculate the log2 of a given natural value, the output is rounded to upper 
	-- arg1: the value to take the log of
	-- return: the log2 value of arg1 
	function log2(value:NATURAL) return NATURAL;
	-- Function to calculate the sign extended value
	-- arg1: original value
	-- arg2: total bus widht of returned std_logic_vector
	-- return: the sign extended std_logic_vector
	function signextend(value:STD_LOGIC_VECTOR;extend_length:NATURAL) return STD_LOGIC_VECTOR;
	-- arguments are always separated by a ′;′
end utils;
--------------------------------------------------------------
package body utils is
	----------------------------------------------------------------------------
	function log2(value:NATURAL) return NATURAL is
		variable counter:NATURAL;	
		variable val:NATURAL;
	begin
		counter:=0;
		val:=value;
		while val>0 loop
			counter:=counter+1;
			val:=val/2;
		end loop;
		return counter;
	end log2;
	----------------------------------------------------------------------------
	function signextend(value:STD_LOGIC_VECTOR;extend_length:NATURAL) return STD_LOGIC_VECTOR is
		variable sign_bit:std_logic;
		variable return_vector:std_logic_vector(extend_length-1 downto 0);
	begin
		sign_bit:=value(value′length-1);
		return_vector(extend_length-1 downto value′length):=(others=>sign_bit);
		return_vector(value′length-1 downto 0):=value;
		return return_vector;
	end signextend;
	----------------------------------------------------------------------------
end utils;
--------------------------------------------------------------

De constanten en dataypes kunnen dan letterlijk gebruikt worden in de VHDL-modules die deze package includen. Dit kan zoals weergegeven in onderstaand codefragment.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
-- include here our own library (package)
library work;
use work.utils.ALL;
----------------------------------------------------------------------------------
entity MUX is	
	port(
		clk:				IN			STD_LOGIC;
		selector:			IN			STD_LOGIC;
		input1:				IN			BUS_st;
		input2:				IN			BUS_st;
		outputsignal:		OUT 		BUS_st
	);
end MUX;
----------------------------------------------------------------------------------
architecture Behavioral of MUX is	
begin
	muxProc:process(clk)	
		
	begin
		if (rising_edge(clk)) then
			if (selector='1') then
				outputsignal<=input2;
			else 
				outputsignal<=input1;
			end if;
		end if;
	end process muxProc;
end Behavioral;
----------------------------------------------------------------------------------

Opgaven

  • Schrijf een functie in een eigen package die toelaat om de log2 van een getal te nemen. De uitkomst wordt steeds naar boven afgerond. Deze functie kan vanaf nu gebruikt worden automatisch de busbreedte te bepalen vanuit een maximale waarde. Dit vermijdt om zelf het rekenwerk manueel te hoeven uit te voeren.
  • Schrijf een functie die toelaat om een sign-extend van een geheel getal uit te voeren. Dit kan handig zijn om een signaal van 16 naar bv. 32 bits om te zetten zonder dat het teken van het getal verloren gaat.
  • Schrijf een stack buffer die toelaat om data als een lifo systeem op te slaan. Kies de nodige parameters en zorg ervoor dat de adresbreedte automatisch ingevuld wordt a.d.h.v. het maximaal aantal elementen.
  • Herneem de opgave van de ringbuffer en parametriseer deze op dezelfde manier als de stack.
  • Schrijf een module die toelaat om een running average uit te rekenen van een aantal gehele getallen. Schrijf hierbij de running average a.d.h.v. de ringbuffer uit de voorgaande opgaven en volgens een recursieve werkwijze. Merk op dat enkel de naam op recursie duidt en er geen sprake is van echte recursie in VHDL.