Hier das klassische Lauflicht in verschiedenen Variationen. Als Voraussetzung wird für das gesamte Design ein 10MHz-Takt verwendet, der mit Clock-Enables jeweils auf die entsprechende Lauflichtgeschwindigkeit angepasst wird. Die Ausgabe erfolgt 8-kanalig z.B. auf LEDs.
Wir haben also folgenden Header mit Entity und Clock-Enable-Erzeugung:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity Clockdivider is
Generic ( fclk : integer := 10000000;
frequenz : integer := 3);
Port ( clk : in STD_LOGIC;
clken : out STD_LOGIC);
end Clockdivider;
architecture Behavioral of Clockdivider is
signal clkdiv : integer range 0 to fclk/frequenz := 0;
begin
process begin
wait until rising_edge(clk);
if (clkdiv < fclk/frequenz) then
clkdiv <= clkdiv+1;
clken <= '0';
else
clkdiv <= 0;
clken <= '1';
end if;
end process;
end Behavioral;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity Lauflicht is
Port ( clk : in STD_LOGIC;
leds : out STD_LOGIC_VECTOR (7 downto 0));
end Lauflicht;
architecture Behavioral of Lauflicht is
component Clockdivider is
Generic ( fclk : integer;
frequenz : integer);
Port ( clk : in STD_LOGIC;
clken : out STD_LOGIC);
end component;
signal clken : std_logic;
:
:
begin
clocken: Clockdivider GENERIC MAP( fclk => 10000000, frequenz => 3)
PORT MAP ( clk => clk , clken => clken );
:
:
:
end Behavioral;
Darin eingefügt wird jeweils an den Punkten (Signale bzw. Code) eines der folgenden Module:
1. Schieberegister, von links nach rechts
signal sr : std_logic_vector (7 downto 0) := "10000000";
:
:
process begin
wait until rising_edge(clk);
if (clken='1') then
if (sr="00000001") then
sr <= "10000000";
else
sr <= '0' & sr(7 downto 1);
end if;
end if;
end process;
leds <= sr;
Hier wird der Vektor sr auf einen Randwert (00000001) verglichen, und beim Erreichen dieses Wertes der Startwert (10000000) geladen.
Einiges eleganter ist die folgende Version. Hier wird einfach das hinten herausfallende Bit vorne wieder eingeschoben:
:
process begin
wait until rising_edge(clk);
if (clken='1') then
sr <= sr(0) & sr(7 downto 1);
end if;
end process;
leds <= sr;
2. Schieberegister, von rechts nach links
signal sr : std_logic_vector (7 downto 0) := "00000001";
:
process begin
wait until rising_edge(clk);
if (clken='1') then
sr <= sr(6 downto 0) & sr(7);
end if;
end process;
leds <= sr;
3. Schieberegister, hin und her (einfacher Knight-Rider)
signal sr : std_logic_vector (7 downto 0) := "00000001";
signal dir : std_logic := '0';
:
:
process begin
wait until rising_edge(clk);
if (clken='1') then
if (dir='0') then
if (sr="01000000") then dir <= not dir; end if;
sr <= sr(6 downto 0) & '0';
else
if (sr="00000010") then dir <= not dir; end if;
sr <= '0' & sr(7 downto 1);
end if;
end if;
end process;
leds <= sr;
4. LUT, konfigurierbar
signal cnt : integer range 0 to 7 := 0;
:
:
process begin
wait until rising_edge(clk);
if (clken='1') then
if (cnt<7) then cnt <= cnt+1;
else cnt <= 0;
end if;
end if;
case cnt is
when 0 => leds <= "10000000"; -- Lauflichtmuster hier ablegen
when 1 => leds <= "00100000";
when 2 => leds <= "00001000";
when 3 => leds <= "00000010";
when 4 => leds <= "00000001";
when 5 => leds <= "00000100";
when 6 => leds <= "00010000";
when others=>leds <= "01000000";
end case;
end process;
Hier gibt es auch die Alternative, diese Zuweisung Concurrent zu schreiben:
process begin
wait until rising_edge(clk);
if (clken='1') then
if (cnt<7) then cnt <= cnt+1;
else cnt <= 0;
end if;
end if;
end process;
leds <= "10000000" when cnt = 0 else
"00100000" when cnt = 1 else
"00001000" when cnt = 2 else
"00000010" when cnt = 3 else
"00000001" when cnt = 4 else
"00000100" when cnt = 5 else
"00010000" when cnt = 6 else
"01000000";
Damit wird eine Registerstufe am Ausgang gespart. Das selbe wäre erreicht, wenn im vorherigen Code die case-Anweisung in einen eigenen kombinatorischen Prozess ausgelagert würde. Der Synthesizer erkennt hier in beiden Beschreibungen ein ROM und fügt dieses in den RTL-Plan ein. Natürlich könnte das ROM auch direkt beschrieben werden, wie im Beispiel 5.
5. ROM, konfigurierbar
signal cnt : integer range 0 to 7 := 0;
:
:
process
type Rom is array (0 to 7) of std_logic_vector(7 downto 0);
constant ledarray : Rom := ("10000000",
"00100000",
"00001000",
"00000010",
"00000001",
"00000100",
"00010000",
"01000000");
begin
wait until rising_edge(clk);
if (clken='1') then
if (cnt<7) then
cnt <= cnt+1;
else
cnt <= 0;
end if;
end if;
leds <= ledarray(cnt);
end process;
Und last but not least, der Traum aller Pontiac Trans Am Fahrer
6. ROM, der echte Knight-Rider (mit nachleuchtenden LEDs)
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity Lauflicht is
Port ( clk : in STD_LOGIC;
leds : out STD_LOGIC_VECTOR (7 downto 0));
end Lauflicht;
architecture Behavioral of Lauflicht is
constant fclk : integer := 50000;
signal clkdiv : integer range 0 to fclk/2000 := 0;-- PWM Frequenz: 2000 Hz / 16 Schritte = 125 Hz
signal nextled : integer range 0 to 5000/3 := 0; -- Weiterschalten mit 3Hz
signal idx : integer range 0 to 13 := 0; -- Index
signal pwmcnt : integer range 0 to 15 := 0; -- PWM Zähler
signal ledbright : std_logic_vector( 7 downto 0); -- die hellen LEDs
signal leddimmed : std_logic_vector( 7 downto 0); -- die etwas dunkler nachleuchtenden LEDs
signal ledglow : std_logic_vector( 7 downto 0); -- die gerade noch so nachleuchtenden LEDs
begin
process
type Rom is array (0 to 13) of std_logic_vector(7 downto 0);
constant ledarray : Rom := ("10000000",
"01000000",
"00100000",
"00010000",
"00001000",
"00000100",
"00000010",
"00000001",
"00000010",
"00000100",
"00001000",
"00010000",
"00100000",
"01000000");
begin
wait until rising_edge(clk);
if (clkdiv<fclk/5000) then -- Taktteiler
clkdiv<=clkdiv+1;
else
clkdiv<=0;
if (nextled<5000/3) then -- Bitmuster verwalten
nextled <= nextled+1;
else
nextled <= 0;
if (idx<13) then idx<=idx+1;
else idx<=0;
end if;
ledglow <= leddimmed;
leddimmed <= ledbright;
ledbright <= ledarray(idx);
end if;
if (pwmcnt<15) then -- PWM für gedimmte LEDs
pwmcnt <= pwmcnt+1;
else
pwmcnt <= 0;
end if;
end if;
end process;
leds <= ledbright or leddimmed or ledglow when pwmcnt < 4 else
ledbright or leddimmed when pwmcnt < 8 else
ledbright;
end Behavioral;
Das Nachleuchten wird mit einer 4 Bit (16 Stufen) PWM mit 125 Hz realisiert. Abhängig von den LEDs sollten die "Nachleuchtwerte" 4 und 8 noch angepasst werden.