Learn FPGA Season 3
اولین برنامه FPGA: چشمک زن LED
قسمت 1: طراحی VHDL یا Verilog
این آموزش ساخت کد VHDL و Verilog را نشان می دهد که LED را در فرکانس مشخصی چشمک می زند.
هر دو VHDL و Verilog نشان داده شدهاند، و شما میتوانید انتخاب کنید که کدامیک را ابتدا میخواهید یاد بگیرید.
هر زمان که کد طراحی نوشته میشود، طراح FPGA باید اطمینان حاصل کند که همانطور که در نظر گرفته شده است کار میکند.
با وجود تمام تلاش شما، همیشه اشتباهاتی در طراحی اولیه شما وجود خواهد داشت.
بهترین راه برای یافتن این اشتباهات در محیط شبیه سازی است.
این آموزش به 2 مرحله تقسیم می شود:
- طراحی HDL
- شبیه سازی HDL
هر دوی این مراحل برای توسعه موفق FPGA بسیار مهم هستند.
گاهی اوقات طراحان FPGA که برای مدتی تحت فشار هستند سعی می کنند از مرحله دوم یعنی شبیه سازی کد خود صرف نظر کنند.
با این حال این یک گام بسیار مهم است!
بدون شبیهسازی مناسب، مجبور خواهید شد کد خود را روی سختافزار اشکال زدایی کنید که میتواند تلاشی بسیار دشوار و زمانبر باشد.
الزامات پروژه:
کد HDL را طراحی کنید که یک LED با فرکانس مشخص 100 هرتز، 50 هرتز، 10 هرتز یا 1 هرتز چشمک بزند.
برای هر یک از فرکانس های چشمک زدن، LED روی 50٪ چرخه کاری تنظیم می شود (نصف زمان روشن است).
فرکانس LED از طریق دو سوئیچ که ورودی FPGA هستند انتخاب می شود.
یک سوئیچ اضافی به نام LED_EN وجود دارد که برای روشن کردن LED باید “1” باشد. FPGA توسط یک نوسانگر 25 مگاهرتز هدایت می شود.
بیایید ابتدا جدول حقیقت را برای انتخابگر فرکانس رسم کنیم:
برای اینکه این کار به درستی کار کند 4 ورودی و 1 خروجی وجود دارد. سیگنال ها عبارتند از:
برای طراحی چهار فرآیند شمارنده وجود دارد که به طور همزمان اجرا می شوند.
این بدان معنی است که همه آنها دقیقاً در یک زمان در حال اجرا هستند.
وظیفه آنها پیگیری تعداد پالس های ساعت مشاهده شده برای هر یک از فرکانس های مختلف است.
حتی اگر سوئیچ ها آن فرکانس خاص را انتخاب نکنند، شمارنده ها همچنان کار می کنند!
این زیبایی طراحی سخت افزار و همزمانی است. همه چیز همیشه اجرا می شود!
درک این موضوع در ابتدا می تواند چالش برانگیز باشد، اما این مفهوم اصلی است که باید به آن تسلط پیدا کنید.
سوئیچ ها فقط برای انتخاب خروجی استفاده می کنند.
آنها چیزی را ایجاد می کنند که به عنوان مالتی پلکسر شناخته می شود.
مالتی پلکسر یا به اختصار mux یک انتخابگر است که یکی از تعدادی ورودی را برای انتشار یا ارسال به خروجی انتخاب می کند.
این یک منطق ترکیبی است، به این معنی که برای کار کردن نیازی به ساعت ندارد.
در زیر بلوک دیاگرام طراحی آمده است. مدتی را صرف فکر کردن به نحوه اجرای این طرح کنید. سعی کنید کد را خودتان بنویسید.
راهی که من برای انجام آن انتخاب کردم در زیر آمده است.
Block Diagram – LED Blink Program
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tutorial_led_blink is
port (
i_clock : in std_logic;
i_enable : in std_logic;
i_switch_1 : in std_logic;
i_switch_2 : in std_logic;
o_led_drive : out std_logic
);
end tutorial_led_blink;
architecture rtl of tutorial_led_blink is
-- Constants to create the frequencies needed:
-- Formula is: (25 MHz / 100 Hz * 50% duty cycle)
-- So for 100 Hz: 25,000,000 / 100 * 0.5 = 125,000
constant c_CNT_100HZ : natural := 125000;
constant c_CNT_50HZ : natural := 250000;
constant c_CNT_10HZ : natural := 1250000;
constant c_CNT_1HZ : natural := 12500000;
-- These signals will be the counters:
signal r_CNT_100HZ : natural range 0 to c_CNT_100HZ;
signal r_CNT_50HZ : natural range 0 to c_CNT_50HZ;
signal r_CNT_10HZ : natural range 0 to c_CNT_10HZ;
signal r_CNT_1HZ : natural range 0 to c_CNT_1HZ;
-- These signals will toggle at the frequencies needed:
signal r_TOGGLE_100HZ : std_logic := '0';
signal r_TOGGLE_50HZ : std_logic := '0';
signal r_TOGGLE_10HZ : std_logic := '0';
signal r_TOGGLE_1HZ : std_logic := '0';
-- One bit select wire.
signal w_LED_SELECT : std_logic;
begin
-- All processes toggle a specific signal at a different frequency.
-- They all run continuously even if the switches are
-- not selecting their particular output.
p_100_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_100HZ = c_CNT_100HZ-1 then -- -1, since counter starts at 0
r_TOGGLE_100HZ <= not r_TOGGLE_100HZ;
r_CNT_100HZ <= 0;
else
r_CNT_100HZ <= r_CNT_100HZ + 1;
end if;
end if;
end process p_100_HZ;
p_50_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_50HZ = c_CNT_50HZ-1 then -- -1, since counter starts at 0
r_TOGGLE_50HZ <= not r_TOGGLE_50HZ;
r_CNT_50HZ <= 0;
else
r_CNT_50HZ <= r_CNT_50HZ + 1;
end if;
end if;
end process p_50_HZ;
p_10_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_10HZ = c_CNT_10HZ-1 then -- -1, since counter starts at 0
r_TOGGLE_10HZ <= not r_TOGGLE_10HZ;
r_CNT_10HZ <= 0;
else
r_CNT_10HZ <= r_CNT_10HZ + 1;
end if;
end if;
end process p_10_HZ;
p_1_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_1HZ = c_CNT_1HZ-1 then -- -1, since counter starts at 0
r_TOGGLE_1HZ <= not r_TOGGLE_1HZ;
r_CNT_1HZ <= 0;
else
r_CNT_1HZ <= r_CNT_1HZ + 1;
end if;
end if;
end process p_1_HZ;
-- Create a multiplexor based on switch inputs
w_LED_SELECT <= r_TOGGLE_100HZ when (i_switch_1 = '0' and i_switch_2 = '0') else
r_TOGGLE_50HZ when (i_switch_1 = '0' and i_switch_2 = '1') else
r_TOGGLE_10HZ when (i_switch_1 = '1' and i_switch_2 = '0') else
r_TOGGLE_1HZ;
-- Only allow o_led_drive to drive when i_enable is high (and gate).
o_led_drive <= w_LED_SELECT and i_enable;
end rtl;
نگاه کلی به کد
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
ابتدا کتابخانههای لازم را باید فراخوانی کنیم.
entity tutorial_led_blink is
port (
i_clock : in std_logic;
i_enable : in std_logic;
i_switch_1 : in std_logic;
i_switch_2 : in std_logic;
o_led_drive : out std_logic
);
end tutorial_led_blink;
در این قسمت ابتدا نام پروژه را تعریف میکنیم و سپس پورت های مورد نیاز را بعلاوه مسیر و نوع آنها را تعریف میکنیم.
architecture rtl of tutorial_led_blink is
-- Constants to create the frequencies needed:
-- Formula is: (25 MHz / 100 Hz * 50% duty cycle)
-- So for 100 Hz: 25,000,000 / 100 * 0.5 = 125,000
constant c_CNT_100HZ : natural := 125000;
constant c_CNT_50HZ : natural := 250000;
constant c_CNT_10HZ : natural := 1250000;
constant c_CNT_1HZ : natural := 12500000;
سپس نام معماری واحد درحال طراحی را انتخاب میکنیم و شروع به تغریف کردن ثابتهای برنامه و سیگنالها میکنیم.
در این قسمت ثابتها بر حسب فرمول نوشته شده برای فرکانسهای موردنیاز محاسبه و درون ثابتذها قرار میدهیم.
-- These signals will be the counters:
signal r_CNT_100HZ : natural range 0 to c_CNT_100HZ;
signal r_CNT_50HZ : natural range 0 to c_CNT_50HZ;
signal r_CNT_10HZ : natural range 0 to c_CNT_10HZ;
signal r_CNT_1HZ : natural range 0 to c_CNT_1HZ;
-- These signals will toggle at the frequencies needed:
signal r_TOGGLE_100HZ : std_logic := '0';
signal r_TOGGLE_50HZ : std_logic := '0';
signal r_TOGGLE_10HZ : std_logic := '0';
signal r_TOGGLE_1HZ : std_logic := '0';
-- One bit select wire.
signal w_LED_SELECT : std_logic;
این قسمت در ادامهی معرفی ثابت ها قرار میگیرد.
در اینجا سیگنال ها به عنوان شمارنده های مورد نیاز تعریف میشوند.
begin
-- All processes toggle a specific signal at a different frequency.
-- They all run continuously even if the switches are
-- not selecting their particular output.
p_100_HZ : process (i_clock) is
begin
if rising_edge(i_clock) then
if r_CNT_100HZ = c_CNT_100HZ-1 then -- -1, since counter starts at 0
r_TOGGLE_100HZ <= not r_TOGGLE_100HZ;
r_CNT_100HZ <= 0;
else
r_CNT_100HZ <= r_CNT_100HZ + 1;
end if;
end if;
end process p_100_HZ;
در این قسمت باید process را تعریف کنیم برای فرکانس مورد نظر و وردودی کلاک را به عنوان حساسیت واحد process معین شود.
سپس به سراغ الگوریتم چشمک زن میرویم.
ابتدا شرط بررسی کلاک را با لبه بالارونده تعیین میکنیم تا هر زمان واحد process به لبه بالا رونده کلاک مواجه شد شرط ما بررسی شود.
در اینجا لازم است زمانی که r_CNT_100HZ
برابر با c_CNT_100HZ-1
میشود r_TOGGLE_100H
تغییر وضعیت بدهد تا LED روشن یا خاموش بشود و سپس r_CNT_100H مجدد برابر با صفر قرار گیرد.
در شرایطی که r_CNT_100HZ برابر با c_CNT_100HZ-1 نبود یک واحد به r_CNT_100HZ اضافه بشود تا زمانی که این دو باهم برابر شوند.
به همین ترتیب برای تمام فرکانس های مورد نظر باید واحد process مختص به خودشان نوشته بشود.
-- Create a multiplexor based on switch inputs
w_LED_SELECT <= r_TOGGLE_100HZ when (i_switch_1 = '0' and i_switch_2 = '0') else
r_TOGGLE_50HZ when (i_switch_1 = '0' and i_switch_2 = '1') else
r_TOGGLE_10HZ when (i_switch_1 = '1' and i_switch_2 = '0') else
r_TOGGLE_1HZ;
-- Only allow o_led_drive to drive when i_enable is high (and gate).
o_led_drive <= w_LED_SELECT and i_enable;
end rtl;
در انتها برای روشن و خاموش شدن LED نیاز به یک مالتی پلکسر جهت استفاده به عنوان سلکتور داریم تا به کمک آن به FPGA دستور دهیم تا با کدام فرکانس کار کند و در نهایت با پالس فعال ساز LED اند (AND) میکنیم.
اولین دیدگاه را ثبت کنید