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
- VHDL-Quelltext des Clockgenerators
- VHDL-Quelltext des Resetgenerators
- VHDL-Quelltext des Prozessors
- VHDL-Quelltext des FPGAs mit RAM-Block
- VHDL-Quelltext des RAM-Blocks
- VHDL-Quelltext des Boards
- Beispiel in VHDL für einen Prozessor mit RAM im FPGA, Teil 1
- Beispiel in VHDL für einen Prozessor mit RAM im FPGA, Teil 2
- Beispiel in VHDL für einen Prozessor mit RAM im FPGA, Teil 3