Sprungnavigation:

zum Inhalt

FPGA mit VHDL

Instanziierung des Clockgenerators

entity clockgenerator is
Port (
     CLK   : out std_logic
     );
end clockgenerator;
architecture arc_clockgenerator of clockgenerator is
constant   clkperiode   : time := 20 ns;
BEGIN
-- ###########################################
-- Clock-Prozesse
-- ###########################################
CLOCKPROC : PROCESS
BEGIN
   CLK <= '1';
   wait for clkperiode / 2;
   CLK <= '0';
   wait for clkperiode / 2;
END PROCESS;
end arc_clockgenerator;

Der komplette VHDL-Quelltext des Clockgenerators kann auch downgeloaded werden.

Instanziierung des Resetgenerators

entity resetgenerator is
Port (
     RST   : out std_logic
     );
end resetgenerator;
architecture arc_resetgenerator of resetgenerator is
constant   resettime   : time := 100 us;
BEGIN
-- ###########################################
-- Reset-Prozesse
-- ###########################################
RESETPROC : PROCESS
BEGIN
   RST <= '1';
   wait for resettime;
   RST <= '0';
   wait;
END PROCESS;
end arc_resetgenerator;

Der komplette VHDL-Quelltext des Resetgenerators kann auch downgeloaded werden.

Instanziierung des Prozessors

Ich liste nur mal die wichtigsten Code-Teile auf, der Rest steht ja in der VHDL-Datei.

So zuerst die beiden Prozeduren für den Buszugriff BUS_WR und BUS_RD.

-- ###########################################
-- Procedur Schreiben auf Bus
-- ###########################################
PROCEDURE BUS_WR (
	signal RST	: in std_logic;
	signal ADDRESS	: in std_logic_vector(7 downto 0);
	signal DATA	: in std_logic_vector(7 downto 0);
	signal T	: out std_logic;
	signal Adressen	: out std_logic_vector(7 downto 0);
	signal DatenOut	: out std_logic_vector(7 downto 0);
	signal nRD	: out std_logic;
	signal nWR	: out std_logic;
	signal nCS	: out std_logic
	) IS
BEGIN
	if (RST = '1') then
		T		<= '0'; -- 0 = Input
		nRD		<= '1';
		nWR		<= '1';
		nCS		<= '1';
		DatenOut	<= (others => '0');
		Adressen	<= (others => '0');
	else
		nRD		<= '1';
		nWR		<= '1';
		nCS		<= '1';
		Adressen	<= ADDRESS;
		wait for	t_ADR_nCS0;
		nCS		<= '0';
		T		<= '1';
		wait for	t_nCS0_DAT;
		DatenOut	<= DATA;
		wait for	t_nCS0_nWR0 - t_nCS0_DAT;
		nWR		<= '0';
		wait for	t_DAT_nWR1;
		nWR		<= '1';
		wait for 	t_nWR1_DAT;
		DatenOut	<= (others => '0');
		wait for	t_nWR1_nCS1;
		nCS		<= '1';
		T		<= '0';
		wait for	t_nCS1_ADR;
	end if;
END BUS_WR;
-- ###########################################
-- Procedur Lesen vom Bus
-- ###########################################
PROCEDURE BUS_RD (
	signal RST	: in std_logic;
	signal ADDRESS	: in std_logic_vector(7 downto 0);
	signal DATA	: out std_logic_vector(7 downto 0);
	signal T	: out std_logic;
	signal Adressen	: out std_logic_vector(7 downto 0);
	signal DatenIn	: in std_logic_vector(7 downto 0);
	signal nRD	: out std_logic;
	signal nWR	: out std_logic;
	signal nCS	: out std_logic
	) IS
BEGIN
	if (RST = '1') then
		T		<= '0'; -- 0 = Input
		nRD		<= '1';
		nWR		<= '1';
		nCS		<= '1';
		Adressen	<= (others => '0');
	else
		nRD		<= '1';
		nWR		<= '1';
		nCS		<= '1';
		Adressen	<= ADDRESS;
		wait for 	t_ADR_nCS0;
		nCS		<= '0';
		wait for	t_nCS0_nRD0;
		nRD		<= '0';
		wait for	t_nCS0_nRD1 - t_nCS0_nRD0;
		nRD		<= '1';
		DATA		<= DatenIn;
		wait for	t_nRD1_DAT;
		wait for	t_nRD1_nCS1;
		nCS		<= '1';
		wait for	t_nCS1_ADR;
		T		 <= '0';
	end if;
END BUS_RD

Prozeduren kann man hier sehr sinnvoll anwenden, weil man es dann einfacher hat die Stimulies zu erstellen:

BUS_WR(RST, ADDRESS, DATA, T, Adressen, DatenOut, nRD, nWR, nCS);
BUS_RD(RST, ADDRESS, DATA, T, Adressen, DatenIn, nRD, nWR, nCS);

Wir benötigen noch IO-Buffer die bidirektional Daten ausgeben können.
Zu den IO-Buffern gibt es unterschiede zwischen ALTERA und XILINX. Lesen Sie bitte dazu den Artikel zu Unterschiede zwischen ALTERA und XILINX.

IOBUF_Bit3 : ALT_IOBUF
generic map (
	IO_STANDARD => "Differential 1.2-V HSTL Class I",
	CURRENT_STRENGTH_NEW => "4mA",
	ENABLE_BUS_HOLD => "none",
	WEAK_PULL_UP_RESISTOR => "off",
	LOCATION => "IOBANK_3C"
	)
port map (
	i => DatenOut(3),
	oe => T,
	o => DatenIn(3),
	io => Daten(3)
	);

Ein Schreibzugriff auf das RAM wird dann so realisiert:

ADDRESS	<= ADDRESS + 1;
DATA	<= x"AA";
wait until(CLK'Event and CLK = '1');
BUS_WR(RST, ADDRESS, DATA, T, Adressen, DatenOut, nRD, nWR, nCS);

Und ein Lesezugriff auf das RAM dann so:

ADDRESS	<= x"01";
wait until(CLK'Event and CLK = '1');
BUS_RD(RST, ADDRESS, DATA, T, Adressen, DatenIn, nRD, nWR, nCS);

Der komplette VHDL-Quelltext des Prozessors kann auch downgeloaded werden.

Instanziierung der IO-Buffer und des RAM im FPGA

Das war eigentlich unsere Kernaufgabe: das RAM am Processor.

Die IO-Buffer erkläre ich hier nicht mehr, da es die Gleichen sind wie schon oben beschrieben.

Für den RAM-Block gibt es sowohl in ALTERA als auch in XLINX Wizards, mit denen man sich passende Speicherblöcke erstellen lassen kann.
Ich habe hier mal eine vom Wizard erzeugte Datei als VHDL-Quelltext des RAM-Blocks zum Downgeload beigestellt.

-- ###########################################
-- Concurrent Statements
-- ###########################################
T <= '1' when (nRD = '0' and nCS = '0') else '0';

-- ###########################################
-- RAM-Speicherzugriff
-- ###########################################
process (CLK, RST)
begin
	if (RST='1') then
		WREN <= '0';
	elsif (CLK'event and CLK='1') then -- CLK rising edge
		WREN <= NOT(nWR) and nRD and NOT(nCS);
	end if;
end process;

Der komplette VHDL-Quelltext des FPGAs mit RAM-Block kann auch downgeloaded werden.

im 3. Teil Simulation weiterlesen.

Alle Informationen und Dateien zu diesem Beispiel

 
Qualitätsmanagement-Stempel von YASKO
Qualitätsmanagement nach
DIN EN ISO 9001:2015
Logo des FED
Mitglied im Fachverband für
Elektronik-Design e.V. (FED)