vhdl
संक्षेप में VHDL का उपयोग कर डिजिटल हार्डवेयर डिजाइन
खोज…
परिचय
इस विषय में हम VHDL के साथ सरल डिजिटल सर्किट को सही ढंग से डिजाइन करने के लिए एक सरल विधि का प्रस्ताव करते हैं। विधि ग्राफिकल ब्लॉक आरेख और एक आसान-से-याद सिद्धांत पर आधारित है:
हार्डवेयर पहले सोचो, कोड VHDL अगला
यह भाषा के संश्लेषण शब्दार्थों की सीमित समझ के साथ, VHDL का उपयोग करके डिजिटल हार्डवेयर डिजाइन में शुरुआती के लिए है।
टिप्पणियों
VHDL का उपयोग करते हुए डिजिटल हार्डवेयर डिज़ाइन सरल है, यहां तक कि शुरुआती लोगों के लिए भी, लेकिन कुछ महत्वपूर्ण चीजें हैं जिन्हें जानना और नियमों का एक छोटा सा सेट पालन करना है। डिजिटल हार्डवेयर में VHDL विवरण को बदलने के लिए प्रयुक्त उपकरण एक लॉजिक सिंथेसाइज़र है। तर्क सिंथेसाइज़र द्वारा उपयोग की जाने वाली VHDL भाषा के शब्दार्थ शब्द लैंग्वेज रेफरेंस मैनुअल (LRM) में वर्णित सिमुलेशन सिमेंटिक्स से अलग हैं। इससे भी बदतर: यह मानकीकृत नहीं है और संश्लेषण उपकरण के बीच भिन्न होता है।
प्रस्तावित विधि सरलता के लिए कई महत्वपूर्ण सीमाओं का परिचय देती है:
- कोई स्तर-ट्रिगर कुंडी नहीं।
- सर्किट एक ही घड़ी के बढ़ते किनारे पर समकालिक होते हैं।
- कोई एसिंक्रोनस रीसेट या सेट नहीं।
- हल संकेतों पर कोई एकाधिक ड्राइव नहीं।
3 की एक श्रृंखला के पहले ब्लॉक आरेख उदाहरण, संक्षेप में डिजिटल हार्डवेयर की मूल बातें प्रस्तुत करता है और एक डिजिटल सर्किट के ब्लॉक आरेख को डिजाइन करने के लिए नियमों की एक छोटी सूची का प्रस्ताव करता है। नियम VHDL कोड के लिए एक सरल अनुवाद की गारंटी देने में मदद करते हैं जो उम्मीद के मुताबिक अनुकरण और संश्लेषित करता है।
कोडिंग उदाहरण एक ब्लॉक आरेख से VHDL कोड में अनुवाद की व्याख्या करता है और इसे एक साधारण डिजिटल सर्किट पर दिखाता है।
अंत में, जॉन कोइली की डिजाइन प्रतियोगिता का उदाहरण दिखाता है कि डिजिटल सर्किट के अधिक जटिल उदाहरण पर प्रस्तावित विधि को कैसे लागू किया जाए। यह भी शुरू की गई सीमाओं पर विस्तृत है और उनमें से कुछ को आराम देता है।
खंड आरेख
डिजिटल हार्डवेयर दो प्रकार के हार्डवेयर आदिम से बनाया गया है:
- कॉम्बिनेटरियल गेट्स (इनवर्टर, और, या, एक्सोर, 1-बिट फुल एडर्स, 1-बिट मल्टीप्लेक्सर्स ...) ये लॉजिक गेट्स अपने इनपुट पर एक साधारण बूलियन कम्प्यूटेशन करते हैं और एक आउटपुट उत्पन्न करते हैं। हर बार जब उनका कोई इनपुट बदलता है, तो वे विद्युत संकेतों को प्रचारित करना शुरू कर देते हैं और थोड़ी देर के बाद, आउटपुट परिणामी मूल्य को स्थिर कर देता है। प्रसार देरी महत्वपूर्ण है क्योंकि यह दृढ़ता से उस गति से संबंधित है जिस पर डिजिटल सर्किट चल सकता है, अर्थात, इसकी अधिकतम घड़ी आवृत्ति।
- मेमोरी एलिमेंट्स (लैचेज, डी-फ्लिप-फ्लॉप, रैम ...)। कॉम्बीनेटरियल लॉजिक गेट्स के विपरीत, स्मृति तत्व अपने किसी भी इनपुट के परिवर्तन पर तुरंत प्रतिक्रिया नहीं करते हैं। उनके पास डेटा इनपुट, कंट्रोल इनपुट और डेटा आउटपुट हैं। वे नियंत्रण इनपुट के एक विशेष संयोजन पर प्रतिक्रिया करते हैं, अपने डेटा इनपुट के किसी भी परिवर्तन पर नहीं। उदाहरण के लिए, राइजिंग-एज ने डी-फ्लिप-फ्लॉप (डीएफएफ) को ट्रिगर किया, जिसमें क्लॉक इनपुट और डेटा इनपुट है। घड़ी के हर बढ़ते किनारे पर, डेटा इनपुट को नमूना किया जाता है और डेटा आउटपुट के लिए कॉपी किया जाता है जो घड़ी के अगले बढ़ते किनारे तक स्थिर रहता है, भले ही डेटा इनपुट बीच में बदल जाए।
एक डिजिटल हार्डवेयर सर्किट कॉम्बिनेटरियल लॉजिक और मेमोरी तत्वों का एक संयोजन है। स्मृति तत्वों की कई भूमिकाएँ होती हैं। उनमें से एक अलग डेटा पर लगातार कई संचालन के लिए एक ही संयोजन तर्क का पुन: उपयोग करने की अनुमति देना है। इसका उपयोग करने वाले सर्किट को अक्सर अनुक्रमिक सर्किट के रूप में संदर्भित किया जाता है। नीचे दिया गया आंकड़ा एक अनुक्रमिक सर्किट का एक उदाहरण दिखाता है जो एक ही दहनशील योजक का उपयोग करके पूर्णांक मानों को जमा करता है, जो एक बढ़ते-ट्रिगर ट्रिगर के लिए धन्यवाद है। यह एक ब्लॉक आरेख का हमारा पहला उदाहरण भी है।
पाइप-लाइनिंग स्मृति तत्वों का एक और सामान्य उपयोग है और कई माइक्रो-प्रोसेसर आर्किटेक्चर का आधार है। इसका उद्देश्य सरल ऑपरेशन के उत्तराधिकार में एक जटिल प्रसंस्करण को विभाजित करके सर्किट की घड़ी की आवृत्ति को बढ़ाना है, और कई लगातार प्रसंस्करण के निष्पादन को अधिकतम करना है:
ब्लॉक आरेख डिजिटल सर्किट का चित्रमय प्रतिनिधित्व है। यह सही निर्णय लेने और कोडिंग से पहले समग्र संरचना की अच्छी समझ प्राप्त करने में मदद करता है। यह कई सॉफ्टवेयर डिजाइन विधियों में अनुशंसित प्रारंभिक विश्लेषण चरणों के बराबर है। अनुभवी डिजाइनर अक्सर इस डिज़ाइन चरण को छोड़ देते हैं, कम से कम सरल सर्किट के लिए। यदि आप डिजिटल हार्डवेयर डिज़ाइन में एक शुरुआत हैं, हालाँकि, और यदि आप VHDL में डिजिटल सर्किट कोड करना चाहते हैं, तो अपने ब्लॉक आरेख को खींचने के लिए नीचे दिए गए 10 सरल नियमों को अपनाकर आपको इसे सही होने में मदद करनी चाहिए:
- एक बड़े आयत के साथ अपनी ड्राइंग को घेरें। यह आपके सर्किट की सीमा है। इस सीमा को पार करने वाली हर चीज एक इनपुट या आउटपुट पोर्ट है। वीएचडीएल इकाई इस सीमा का वर्णन करेगी।
- स्पष्ट रूप से अलग-अलग किनारे वाले रजिस्टरों (जैसे स्क्वायर ब्लॉक) को कॉम्बिनेटरियल लॉजिक (जैसे गोल ब्लॉक) से अलग करते हैं। वीएचडीएल में उन्हें प्रक्रियाओं में अनुवाद किया जाएगा, लेकिन दो अलग-अलग प्रकारों में: तुल्यकालिक और दहनशील।
- स्तर-ट्रिगर लैच का उपयोग न करें, केवल बढ़ते-किनारे ट्रिगर रजिस्टरों का उपयोग करें। यह बाधा वीएचडीएल से नहीं आती है, जो कि मॉडल लेचेस के लिए पूरी तरह से उपयोग करने योग्य है। यह शुरुआती लोगों के लिए एक उचित सलाह है। Latches की कम बार आवश्यकता होती है और उनके उपयोग से कई समस्याएं होती हैं जिनसे हमें संभवतः बचना चाहिए, कम से कम हमारे पहले डिजाइनों के लिए।
- अपने बढ़ते-बढ़ते ट्रिगर रजिस्टरों के लिए एक ही एकल घड़ी का उपयोग करें। वहाँ फिर से, यह अड़चन यहाँ सादगी के लिए है। यह वीएचडीएल से नहीं आता है, जो मल्टी-क्लॉक सिस्टम को मॉडल करने के लिए पूरी तरह से उपयोग करने योग्य है। घड़ी
clock
नामclock
। यह बाहर से आता है और सभी वर्ग ब्लॉकों और केवल उन्हीं का एक इनपुट है। यदि आप चाहें, तो घड़ी का प्रतिनिधित्व भी न करें, यह सभी वर्ग ब्लॉकों के लिए समान है और आप इसे अपने आरेख में निहित छोड़ सकते हैं। - नामित और उन्मुख तीरों के साथ ब्लॉक के बीच संचार का प्रतिनिधित्व करते हैं। ब्लॉक के लिए एक तीर आता है, तीर एक आउटपुट है। ब्लॉक के लिए एक तीर जाता है, तीर एक इनपुट है। ये सभी तीर VHDL इकाई के बंदरगाह बन जाएंगे, यदि वे बड़ी आयत, या VHDL वास्तुकला के संकेतों को पार कर रहे हैं।
- तीर में एक ही मूल है लेकिन उनके पास कई गंतव्य हो सकते हैं। वास्तव में, यदि एक तीर में कई मूल होते हैं तो हम कई ड्राइवरों के साथ एक VHDL सिग्नल बनाएंगे। यह पूरी तरह से असंभव नहीं है लेकिन शॉर्ट-सर्किट से बचने के लिए विशेष देखभाल की आवश्यकता होती है। इस प्रकार हम अभी से इससे बचेंगे। यदि किसी तीर में कई गंतव्य हैं, तो तीर को आवश्यकतानुसार कई बार कांटे। कनेक्टेड और नॉन-कनेक्टेड क्रॉसिंग को भेद करने के लिए डॉट्स का उपयोग करें।
- कुछ तीर बड़े आयत के बाहर से आते हैं। ये इकाई के इनपुट पोर्ट हैं। एक इनपुट एरो आपके किसी भी ब्लॉक का आउटपुट नहीं हो सकता है। यह वीएचडीएल भाषा द्वारा लागू किया गया है: एक इकाई के इनपुट बंदरगाहों को पढ़ा जा सकता है लेकिन लिखा नहीं जाता है। यह शॉर्ट-सर्किट से बचने के लिए फिर से है।
- कुछ तीर बाहर जाते हैं। ये आउटपुट पोर्ट हैं। 2008 से पहले के VHDL संस्करणों में एक इकाई के आउटपुट पोर्ट लिखे जा सकते हैं लेकिन पढ़े नहीं जाते। इस प्रकार एक आउटपुट तीर में एक एकल मूल और एक एकल गंतव्य होना चाहिए: बाहर। आउटपुट तीर पर कोई कांटा नहीं, एक आउटपुट तीर आपके किसी ब्लॉक का इनपुट भी नहीं हो सकता। यदि आप अपने कुछ ब्लॉकों के इनपुट के रूप में आउटपुट तीर का उपयोग करना चाहते हैं, तो इसे दो भागों में विभाजित करने के लिए एक नया गोल ब्लॉक डालें: आंतरिक एक, जितने चाहें उतने कांटे और नए से आने वाला आउटपुट तीर ब्लॉक करो और बाहर जाओ। नया ब्लॉक VHDL में एक सरल निरंतर असाइनमेंट बन जाएगा। एक प्रकार का पारदर्शी नामकरण। चूंकि VHDL 2008 ouptut बंदरगाहों को भी पढ़ा जा सकता है।
- सभी तीर जो / बाहर से / बाहर नहीं आते हैं या आंतरिक संकेत हैं। आप उन सभी को VHDL आर्किटेक्चर में घोषित करेंगे।
- आरेख में प्रत्येक चक्र में कम से कम एक वर्ग ब्लॉक शामिल होना चाहिए। यह VHDL के कारण नहीं है। यह डिजिटल हार्डवेयर डिज़ाइन के मूल सिद्धांतों से आता है। संयुक्त छोरों से पूरी तरह से बचा जाना चाहिए। बहुत ही दुर्लभ मामलों को छोड़कर, वे कोई उपयोगी परिणाम नहीं देते हैं। और ब्लॉक आरेख का एक चक्र जिसमें केवल गोल ब्लॉक शामिल होंगे, एक दहनशील लूप होगा।
अंतिम नियम को ध्यान से जांचना न भूलें, यह दूसरों की तरह ही आवश्यक है लेकिन इसे सत्यापित करना थोड़ा कठिन हो सकता है।
जब तक आपको पूरी तरह से उन विशेषताओं की आवश्यकता नहीं होती है जिन्हें हमने अभी के लिए बाहर रखा है, जैसे कि कई ड्राइवरों के साथ लाच, मल्टीपल-क्लॉक या सिग्नल, आपको आसानी से अपने सर्किट का एक ब्लॉक आरेख खींचना चाहिए जो 10 नियमों का अनुपालन करता है। यदि नहीं, तो समस्या संभवतः उस सर्किट के साथ है जिसे आप वीएचडीएल या तर्क सिंथेसाइज़र के साथ नहीं चाहते हैं। और इसका शायद मतलब है कि जो सर्किट आप चाहते हैं वह डिजिटल हार्डवेयर नहीं है ।
अनुक्रमिक सर्किट के हमारे उदाहरण के लिए 10 नियमों को लागू करने से एक ब्लॉक आरेख होगा:
- आरेख के चारों ओर बड़ी आयत VHDL इकाई के इनपुट और आउटपुट पोर्ट का प्रतिनिधित्व करते हुए, 3 तीरों द्वारा पार की जाती है।
- ब्लॉक आरेख में दो राउंड (कॉम्बीनेटरियल) ब्लॉक होते हैं - योजक और आउटपुट रीनेमिंग ब्लॉक - और एक वर्ग (सिंक्रोनस) ब्लॉक - रजिस्टर।
- यह केवल किनारे-ट्रिगर रजिस्टरों का उपयोग करता है।
- केवल एक घड़ी है, जिसका नाम
clock
और हम केवल इसके बढ़ते किनारे का उपयोग करते हैं। - ब्लॉक आरेख में पांच तीर हैं, एक कांटा के साथ। वे दो आंतरिक संकेतों, दो इनपुट पोर्ट और एक आउटपुट पोर्ट के अनुरूप हैं।
- सभी तीर एक मूल और नामित तीर को छोड़कर एक गंतव्य है
Sum
दो स्थलों है। -
Data_in
औरClock
तीर हमारे दो इनपुट पोर्ट हैं। वे हमारे अपने ब्लॉकों का उत्पादन नहीं कर रहे हैं। -
Data_out
arrow हमारा आउटपुट पोर्ट है। 2008 से पहले VHDL संस्करणों के साथ संगत होने के लिए, हमनेSum
औरData_out
बीच एक अतिरिक्त नामकरण (राउंड) ब्लॉकData_out
। तो,Data_out
पास एक स्रोत और एक गंतव्य है। -
Sum
औरNext_sum
हमारे दो आंतरिक संकेत हैं। - ग्राफ में एक चक्र होता है और इसमें एक वर्ग ब्लॉक होता है।
हमारा ब्लॉक आरेख 10 नियमों का अनुपालन करता है। कोडिंग उदाहरण से विस्तार होगा कि इस प्रकार के ब्लॉक आरेखों का VHDL में अनुवाद कैसे किया जाए।
कोडिंग
यह उदाहरण 3. की एक श्रृंखला का दूसरा है। यदि आपने अभी तक नहीं किया है, तो पहले ब्लॉक आरेख उदाहरण पढ़ें।
एक ब्लॉक आरेख जो 10 नियमों का अनुपालन करता है ( ब्लॉक आरेख उदाहरण देखें) के साथ, VHDL कोडिंग सीधी हो जाती है:
- बड़े आसपास की आयत VHDL इकाई बन जाती है,
- आंतरिक तीर VHDL सिग्नल बन जाते हैं और वास्तुकला में घोषित किए जाते हैं,
- हर वर्ग ब्लॉक वास्तुकला शरीर में एक तुल्यकालिक प्रक्रिया बन जाता है,
- आर्किटेक्चर बॉडी में हर राउंड ब्लॉक एक कोम्बिनेटरियल प्रक्रिया बन जाती है।
आइए हम इसे एक अनुक्रमिक सर्किट के ब्लॉक आरेख पर चित्रित करते हैं:
एक सर्किट के VHDL मॉडल में दो संकलन इकाइयां शामिल हैं:
- वह इकाई जो सर्किट के नाम और उसके इंटरफेस (बंदरगाहों के नाम, दिशा और प्रकार) का वर्णन करती है। यह ब्लॉक आरेख के बड़े आसपास के आयत का सीधा अनुवाद है। यह मानते हुए कि डेटा पूर्णांक हैं, और
clock
VHDL प्रकारbit
(केवल दो मान:'0'
और'1'
) का उपयोग करती है, हमारी अनुक्रमिक सर्किट की इकाई हो सकती है:
entity sequential_circuit is
port(
Data_in: in integer;
Clock: in bit;
Data_out: out integer
);
end entity sequential_circuit;
- आर्किटेक्चर जो सर्किट के इंटर्नल का वर्णन करता है (यह क्या करता है)। यह वह जगह है जहां आंतरिक संकेतों को घोषित किया जाता है और जहां सभी प्रक्रियाओं को त्वरित किया जाता है। हमारे अनुक्रमिक सर्किट की वास्तुकला का कंकाल हो सकता है:
architecture ten_rules of sequential_circuit is
signal Sum, Next_sum: integer;
begin
<...processes...>
end architecture ten_rules;
हमारे पास आर्किटेक्चर बॉडी, एक सिंक्रोनस (स्क्वायर ब्लॉक) और दो कॉम्बिनेटरियल (गोल ब्लॉक) को जोड़ने के लिए तीन प्रक्रियाएं हैं।
एक तुल्यकालिक प्रक्रिया इस तरह दिखती है:
process(clock)
begin
if rising_edge(clock) then
o1 <= i1;
...
ox <= ix;
end if;
end process;
जहाँ i1, i2,..., ix
सभी तीर हैं जो आरेख के संबंधित वर्ग ब्लॉक में प्रवेश करते हैं और o1, ..., ox
सभी तीर हैं जो आरेख के संगत वर्ग ब्लॉक का उत्पादन करते हैं। निश्चित रूप से संकेतों के नामों को छोड़कर, कुछ भी नहीं बदला जाएगा। कुछ भी तो नहीं। एक भी पात्र नहीं।
हमारे उदाहरण की तुल्यकालिक प्रक्रिया इस प्रकार है:
process(clock)
begin
if rising_edge(clock) then
Sum <= Next_sum;
end if;
end process;
जिसे अनौपचारिक रूप से अनुवादित किया जा सकता है: यदि clock
बदलती है, और केवल तभी, यदि परिवर्तन एक बढ़ती बढ़त ( '0'
से '1'
) है, तो Sum
को संकेत करने के लिए संकेत Next_sum
का मान असाइन करें।
एक संयोजन प्रक्रिया इस तरह दिखती है:
process(i1, i2,... , ix)
variable v1: <type_of_v1>;
...
variable vy: <type_of_vy>;
begin
v1 := <default_value_for_v1>;
...
vy := <default_value_for_vy>;
o1 <= <default_value_for_o1>;
...
oz <= <default_value_for_oz>;
<statements>
end process;
जहाँ i1, i2,..., in
सभी तीर हैं जो आरेख के संगत गोल ब्लॉक में प्रवेश करते हैं। सभी और नहीं। हम किसी भी तीर को नहीं भूलेंगे और हम सूची में कुछ और नहीं जोड़ेंगे।
v1, ..., vy
चर हैं जिन्हें हमें प्रक्रिया के कोड को सरल बनाने की आवश्यकता हो सकती है। उनकी बिल्कुल वैसी ही भूमिका है जैसी किसी अन्य अनिवार्य प्रोग्रामिंग भाषा में: अस्थायी मूल्यों की। उन्हें पढ़ने से पहले बिल्कुल सौंपा जाना चाहिए। यदि हम इसकी गारंटी देने में विफल रहते हैं, तो प्रक्रिया किसी भी तरह से अधिक नहीं होगी क्योंकि यह एक प्रक्रिया निष्पादन से अगले तक कुछ चर के मूल्य को बनाए रखने के लिए स्मृति तत्वों के मॉडल को बनाएगा। यह vi := <default_value_for_vi>
प्रक्रिया की शुरुआत में कथन का कारण है। ध्यान दें कि <default_value_for_vi>
निरंतर होना चाहिए। यदि नहीं, यदि वे अभिव्यक्ति हैं, तो हम गलती से अभिव्यक्ति में चर का उपयोग कर सकते हैं और इसे असाइन करने से पहले एक चर पढ़ सकते हैं।
o1, ..., om
सभी तीर हैं जो आपके आरेख के संबंधित गोल ब्लॉक का उत्पादन करते हैं। सभी और नहीं। प्रक्रिया निष्पादन के दौरान उन्हें कम से कम एक बार पूरी तरह से सौंपा जाना चाहिए। VHDL नियंत्रण संरचनाओं के रूप में ( if
, case
...) बहुत आसानी से आउटपुट सिग्नल को असाइन करने से रोक सकता है, तो हम प्रक्रिया की शुरुआत में, उनमें से प्रत्येक को बिना शर्त के, बिना किसी स्थिर मान <default_value_for_oi>
साथ असाइन करने की दृढ़ता से सलाह देते हैं। इस तरह, यहां तक कि एक अगर if
बयान मास्क एक संकेत काम है, यह एक मूल्य वैसे भी प्राप्त हुआ होगा।
इस VHDL कंकाल के लिए बिल्कुल कुछ भी नहीं बदला जाएगा, चरों के नाम के अलावा, यदि कोई हो, इनपुट्स के नाम, आउटपुट के नाम, <default_value_for_..>
स्थिरांक और <statements>
के मान। यदि आप ऐसा संश्लेषण अवांछित स्मृति तत्वों (सबसे अधिक संभावना लैच) का अनुमान लगा होगा एक भी डिफ़ॉल्ट मान असाइनमेंट याद रखें, और परिणाम नहीं होगा क्या आप शुरू में करना चाहता था।
हमारे उदाहरण अनुक्रमिक सर्किट में, दहनशील योजक प्रक्रिया है:
process(Sum, Data_in)
begin
Next_sum <= 0;
Next_sum <= Sum + Data_in;
end process;
जिसे अनौपचारिक रूप से अनुवादित किया जा सकता है: यदि Sum
या Data_in
(या दोनों) परिवर्तन मान 0 को संकेत करने के लिए असाइन करते हैं Next_sum
और फिर इसे फिर से असाइन करें Sum + Data_in
।
पहले असाइनमेंट के रूप में (लगातार डिफ़ॉल्ट मान 0
) तुरंत एक और असाइनमेंट होता है जो इसे अधिलेखित करता है, हम इसे सरल कर सकते हैं:
process(Sum, Data_in)
begin
Next_sum <= Sum + Data_in;
end process;
दूसरी कॉम्बिनेटोरियल प्रक्रिया उस गोल ब्लॉक से मेल खाती है जिसे हमने VHDL संस्करणों से पहले 2008 में पालन करने के लिए एक से अधिक गंतव्य के साथ एक आउटपुट तीर पर जोड़ा था। इसका कोड बस है:
process(Sum)
begin
Data_out <= 0;
Data_out <= Sum;
end process;
अन्य कॉम्बिनेटरियल प्रक्रिया के समान कारण के लिए, हम इसे सरल बना सकते हैं:
process(Sum)
begin
Data_out <= Sum;
end process;
अनुक्रमिक सर्किट का पूरा कोड है:
-- File sequential_circuit.vhd
entity sequential_circuit is
port(
Data_in: in integer;
Clock: in bit;
Data_out: out integer
);
end entity sequential_circuit;
architecture ten_rules of sequential_circuit is
signal Sum, Next_sum: integer;
begin
process(clock)
begin
if rising_edge(clock) then
Sum <= Next_sum;
end if;
end process;
process(Sum, Data_in)
begin
Next_sum <= Sum + Data_in;
end process;
process(Sum)
begin
Data_out <= Sum;
end process;
end architecture ten_rules;
नोट: हम किसी भी क्रम में तीन प्रक्रियाओं को लिख सकते हैं, यह सिमुलेशन में या संश्लेषण में अंतिम परिणाम के लिए कुछ भी नहीं बदलेगा। ऐसा इसलिए है क्योंकि तीन प्रक्रियाएं समवर्ती कथन हैं और VHDL उनके साथ ऐसा व्यवहार करता है जैसे कि वे वास्तव में समानांतर थे।
जॉन कोइली की डिजाइन प्रतियोगिता
यह उदाहरण सीधे जॉन कूली के SNUG'95 (सिनॉप्स यूजर्स ग्रुप मीटिंग) की डिज़ाइन प्रतियोगिता से लिया गया है। प्रतियोगिता का उद्देश्य एक ही डिज़ाइन समस्या पर VHDL और वेरिलॉग डिजाइनरों का विरोध करना था। जॉन के दिमाग में यह निर्धारित करने के लिए कि क्या भाषा सबसे अधिक कुशल थी। परिणाम यह था कि 9 में से 8 वेरिलॉग डिज़ाइनर डिज़ाइन प्रतियोगिता को पूरा करने में कामयाब रहे, फिर भी 5 में से कोई भी वीएचडीएल डिज़ाइनर नहीं बन सका। उम्मीद है, प्रस्तावित पद्धति का उपयोग करते हुए, हम बहुत बेहतर काम करेंगे।
विशेष विवरण
हमारा लक्ष्य सादे सिंथेसिबल वीएचडीएल (इकाई और वास्तुकला) को एक सिंक्रोनस अप-बाय -3, डाउन-बाय -5, लोड करने योग्य, मापांक 512 काउंटर, कैरी आउटपुट, उधार आउटपुट और समता आउटपुट के साथ डिजाइन करना है। काउंटर एक 9 बिट्स अहस्ताक्षरित काउंटर है इसलिए यह 0 से 511 के बीच होता है। काउंटर का इंटरफ़ेस विनिर्देश निम्न तालिका में दिया गया है:
नाम | बिट-चौड़ाई | दिशा | विवरण |
---|---|---|---|
घड़ी | 1 | इनपुट | मास्टर घड़ी; काउंटर CLOCK के बढ़ते किनारे पर सिंक्रनाइज़ है |
डि | 9 | इनपुट | डेटा इनपुट बस; काउंटर को DI के साथ लोड किया जाता है जब UP और DOWN दोनों कम होते हैं |
यूपी | 1 | इनपुट | अप-बाय -3 काउंट कमांड; जब उत्तर प्रदेश उच्च और नीचे 3 से काउंटर वेतन वृद्धि है, अपने अधिकतम मूल्य (511) के आसपास लपेटता है |
नीचे | 1 | इनपुट | डाउन-बाय -5 काउंट कमांड; जब DOWN ऊंचा हो और UP 5 से कम हो जाए, तो उसके न्यूनतम मूल्य (0) के आसपास लपेटकर |
सीओ | 1 | उत्पादन | संकेत बाहर ले; उच्च केवल तभी जब अधिकतम मान (511) से परे गिना जाए और इस तरह से लपेटा जाए |
बो | 1 | उत्पादन | संकेत बाहर उधार; उच्च केवल तभी जब न्यूनतम मान (0) से नीचे गिना जाता है और इस प्रकार लपेटता है |
कर | 9 | उत्पादन | आउटपुट बस; काउंटर का वर्तमान मूल्य; जब UP और DOWN दोनों उच्च होते हैं तो काउंटर इसके मान को बनाए रखता है |
पीओ | 1 | उत्पादन | समता बाहर संकेत; उच्च जब काउंटर के वर्तमान मूल्य में 1 की संख्या होती है |
जब अपने अधिकतम मूल्य से परे या जब अपने न्यूनतम मूल्य से नीचे की गिनती करते हैं तो काउंटर चारों ओर घूमता है:
काउंटर वर्तमान मूल्य | ऊपर नीचे | काउंटर अगला मान | अगला सीओ | अगला बीओ | अगला PO |
---|---|---|---|---|---|
एक्स | 00 | डि | 0 | 0 | समता (डीआई) |
एक्स | 1 1 | एक्स | 0 | 0 | समता (एक्स) |
0 0 x ≤ 508 | 10 | x + 3 | 0 | 0 | समता (x + 3) |
509 | 10 | 0 | 1 | 0 | 1 |
510 | 10 | 1 | 1 | 0 | 0 |
511 | 10 | 2 | 1 | 0 | 0 |
5 ≤ x ≤ 511 | 01 | एक्स-5 | 0 | 0 | समता (एक्स 5) |
4 | 01 | 511 | 0 | 1 | 0 |
3 | 01 | 510 | 0 | 1 | 1 |
2 | 01 | 509 | 0 | 1 | 1 |
1 | 01 | 508 | 0 | 1 | 0 |
0 | 01 | 507 | 0 | 1 | 1 |
खंड आरेख
इन विशिष्टताओं के आधार पर हम एक ब्लॉक आरेख डिजाइन करना शुरू कर सकते हैं। आइए हम पहले इंटरफ़ेस का प्रतिनिधित्व करते हैं:
हमारे सर्किट में 4 इनपुट (घड़ी सहित) और 4 आउटपुट हैं। अगला कदम यह तय करने में है कि हम कितने रजिस्टरों और कॉम्बीनेटरियल ब्लॉक का उपयोग करेंगे और उनकी भूमिका क्या होगी। इस सरल उदाहरण के लिए हम एक कॉम्बीनेटरियल ब्लॉक को काउंटर के अगले मूल्य की गणना, कैरी आउट और उधार के लिए समर्पित करेंगे। समता बाहर के अगले मूल्य की गणना करने के लिए एक और संयुक्त ब्लॉक का उपयोग किया जाएगा। काउंटर, कैरी आउट और लोन आउट के वर्तमान मूल्यों को एक रजिस्टर में संग्रहीत किया जाएगा, जबकि समता के वर्तमान मूल्य को एक अलग रजिस्टर में संग्रहीत किया जाएगा। परिणाम नीचे दिए गए आंकड़े पर दिखाया गया है:
यह जांचना कि ब्लॉक आरेख हमारे 10 डिजाइन नियमों का अनुपालन करता है, जल्दी से किया जाता है:
- हमारे बाहरी इंटरफ़ेस को बड़े आसपास के आयत द्वारा ठीक से दर्शाया गया है।
- हमारे 2 कॉम्बीनेटरियल ब्लॉक (गोल) और हमारे 2 रजिस्टर (वर्ग) स्पष्ट रूप से अलग हैं।
- हम केवल उभरे हुए किनारे वाले रजिस्टरों का उपयोग करते हैं।
- हम केवल एक घड़ी का उपयोग करते हैं।
- हमारे पास 4 आंतरिक तीर (सिग्नल), 4 इनपुट तीर (इनपुट पोर्ट) और 4 आउटपुट तीर (आउटपुट पोर्ट) हैं।
- हमारे किसी भी तीर में कई मूल नहीं हैं। तीन में कई गंतव्य (
clock
,ncnt
औरdo
) हैं। - हमारे 4 इनपुट तीरों में से कोई भी हमारे आंतरिक ब्लॉक का आउटपुट नहीं है।
- हमारे उत्पादन के तीन तीरों में एक मूल और एक गंतव्य है। लेकिन हमारे
do
2 गंतव्य हैं: बाहर और हमारे कॉम्बिनेटरियल ब्लॉक में से एक। यह नियम संख्या 8 का उल्लंघन करता है और यदि हम VHDL संस्करणों से पहले अनुपालन करना चाहते हैं, तो एक नया संयोजन ब्लॉक डालकर तय किया जाना चाहिए:
- अब हमारे पास बिल्कुल 5 आंतरिक सिग्नल (
cnt
,nco
,nbo
,ncnt
औरnpo
) हैं। - आरेख में केवल एक चक्र होता है, जो
cnt
औरncnt
द्वारा गठितncnt
। चक्र में एक वर्ग ब्लॉक है।
2008 से पहले VHDL संस्करणों में कोडिंग
VHDL में हमारे ब्लॉक आरेख का अनुवाद सीधा है। काउंटर का वर्तमान मान 0 से 511 तक है, इसलिए हम इसका प्रतिनिधित्व करने के लिए 9-बिट्स bit_vector
सिग्नल का उपयोग करेंगे। एकमात्र सूक्ष्मता बिट डेटा (जैसे समता की गणना) और एक ही डेटा पर अंकगणितीय संचालन करने की आवश्यकता से आती है। मानक numeric_bit
पुस्तकालय के पैकेज ieee
को हल करती है यह: यह एक वाणी unsigned
के रूप में बिल्कुल वैसा ही घोषणा के साथ प्रकार bit_vector
और भार के अंकगणितीय ऑपरेटर इस तरह है कि वे के किसी भी मिश्रण ले unsigned
और पूर्णांकों। बाहर ले जाने और उधार लेने की गणना करने के लिए हम 10-बिट unsigned
अस्थायी मान का उपयोग करेंगे।
पुस्तकालय घोषणाएँ और इकाई:
library ieee;
use ieee.numeric_bit.all;
entity cooley is
port(
clock: in bit;
up: in bit;
down: in bit;
di: in bit_vector(8 downto 0);
co: out bit;
bo: out bit;
po: out bit;
do: out bit_vector(8 downto 0)
);
end entity cooley;
वास्तुकला का कंकाल है:
architecture arc1 of cooley is
signal cnt: unsigned(8 downto 0);
signal ncnt: unsigned(8 downto 0);
signal nco: bit;
signal nbo: bit;
signal npo: bit;
begin
<...processes...>
end architecture arc1;
हमारे 5 ब्लॉकों में से प्रत्येक को एक प्रक्रिया के रूप में तैयार किया गया है। हमारे दो रजिस्टरों के लिए समकालिक प्रक्रियाओं को कोड करना बहुत आसान है। हम केवल कोडिंग उदाहरण में प्रस्तावित पैटर्न का उपयोग करते हैं। उदाहरण के लिए, समानता को ध्वज को संग्रहीत करने वाले रजिस्टर को कोडित किया गया है:
poreg: process(clock)
begin
if rising_edge(clock) then
po <= npo;
end if;
end process poreg;
और co
, bo
और cnt
स्टोर करने वाला दूसरा रजिस्टर:
cobocntreg: process(clock)
begin
if rising_edge(clock) then
co <= nco;
bo <= nbo;
cnt <= ncnt;
end if;
end process cobocntreg;
नाम बदलने की प्रक्रिया भी बहुत सरल है:
rename: process(cnt)
begin
do <= (others => '0');
do <= bit_vector(cnt);
end process rename;
समता संगणना एक चर और एक साधारण लूप का उपयोग कर सकती है:
parity: process(ncnt)
variable tmp: bit;
begin
tmp := '0';
npo <= '0';
for i in 0 to 8 loop
tmp := tmp xor ncnt(i);
end loop;
npo <= not tmp;
end process parity;
अंतिम संयोजन प्रक्रिया सभी का सबसे जटिल है, लेकिन प्रस्तावित अनुवाद पद्धति को सख्ती से लागू करना आसान भी बनाता है:
u3d5: process(up, down, di, cnt)
variable tmp: unsigned(9 downto 0);
begin
tmp := (others => '0');
nco <= '0';
nbo <= '0';
ncnt <= (others => '0');
if up = '0' and down = '0' then
ncnt <= unsigned(di);
elsif up = '1' and down = '1' then
ncnt <= cnt;
elsif up = '1' and down = '0' then
tmp := ('0' & cnt) + 3;
ncnt <= tmp(8 downto 0);
nco <= tmp(9);
elsif up = '0' and down = '1' then
tmp := ('0' & cnt) - 5;
ncnt <= tmp(8 downto 0);
nbo <= tmp(9);
end if;
end process u3d5;
ध्यान दें कि दो सिंक्रोनस प्रक्रियाओं को भी मिलाया जा सकता है और हमारी एक कॉम्बीनेटरियल प्रक्रियाओं को सरल समवर्ती सिग्नल असाइनमेंट में सरल बनाया जा सकता है। पुस्तकालय और संकुल घोषणाओं के साथ और प्रस्तावित सरलीकरण के साथ पूरा कोड इस प्रकार है:
library ieee;
use ieee.numeric_bit.all;
entity cooley is
port(
clock: in bit;
up: in bit;
down: in bit;
di: in bit_vector(8 downto 0);
co: out bit;
bo: out bit;
po: out bit;
do: out bit_vector(8 downto 0)
);
end entity cooley;
architecture arc2 of cooley is
signal cnt: unsigned(8 downto 0);
signal ncnt: unsigned(8 downto 0);
signal nco: bit;
signal nbo: bit;
signal npo: bit;
begin
reg: process(clock)
begin
if rising_edge(clock) then
co <= nco;
bo <= nbo;
po <= npo;
cnt <= ncnt;
end if;
end process reg;
do <= bit_vector(cnt);
parity: process(ncnt)
variable tmp: bit;
begin
tmp := '0';
npo <= '0';
for i in 0 to 8 loop
tmp := tmp xor ncnt(i);
end loop;
npo <= not tmp;
end process parity;
u3d5: process(up, down, di, cnt)
variable tmp: unsigned(9 downto 0);
begin
tmp := (others => '0');
nco <= '0';
nbo <= '0';
ncnt <= (others => '0');
if up = '0' and down = '0' then
ncnt <= unsigned(di);
elsif up = '1' and down = '1' then
ncnt <= cnt;
elsif up = '1' and down = '0' then
tmp := ('0' & cnt) + 3;
ncnt <= tmp(8 downto 0);
nco <= tmp(9);
elsif up = '0' and down = '1' then
tmp := ('0' & cnt) - 5;
ncnt <= tmp(8 downto 0);
nbo <= tmp(9);
end if;
end process u3d5;
end architecture arc2;
थोड़ा और आगे बढ़ें
प्रस्तावित विधि सरल और सुरक्षित है, लेकिन यह कई बाधाओं पर निर्भर करता है जिन्हें आराम दिया जा सकता है।
ब्लॉक आरेख ड्राइंग को छोड़ दें
अनुभवी डिजाइनर सरल डिजाइनों के लिए एक ब्लॉक आरेख को आकर्षित कर सकते हैं। लेकिन वे अभी भी पहले हार्डवेयर सोचते हैं। वे कागज की एक शीट के बजाय अपने सिर में खींचते हैं लेकिन वे किसी भी तरह ड्राइंग जारी रखते हैं।
अतुल्यकालिक रीसेट का उपयोग करें
ऐसी परिस्थितियां हैं जहां अतुल्यकालिक रीसेट (या सेट) एक डिजाइन की गुणवत्ता में सुधार कर सकते हैं। प्रस्तावित विधि केवल तुल्यकालिक रीसेट का समर्थन करती है (जो कि ऐसे सेट हैं जिन्हें घड़ी के बढ़ते किनारों पर ध्यान में रखा जाता है):
process(clock)
begin
if rising_edge(clock) then
if reset = '1' then
o <= reset_value_for_o;
else
o <= i;
end if;
end if;
end process;
अतुल्यकालिक रीसेट के साथ संस्करण संवेदनशीलता सूची में रीसेट सिग्नल को जोड़कर और इसे सर्वोच्च प्राथमिकता देकर हमारे टेम्पलेट को संशोधित करता है:
process(clock, reset)
begin
if reset = '1' then
o <= reset_value_for_o;
elsif rising_edge(clock) then
o <= i;
end if;
end process;
कई सरल प्रक्रियाओं को मिलाएं
हमने पहले ही अपने उदाहरण के अंतिम संस्करण में इसका उपयोग किया था। कई सिंक्रोनस प्रक्रियाओं को विलय करना, यदि वे सभी एक ही घड़ी हैं, तो तुच्छ है। एक में कई दहनशील प्रक्रियाओं को जोड़ना भी तुच्छ है और ब्लॉक आरेख का सिर्फ एक सरल पुनर्गठन है।
हम समकालिक प्रक्रियाओं के साथ कुछ दहनशील प्रक्रियाओं का विलय भी कर सकते हैं। लेकिन ऐसा करने के लिए हमें अपने ब्लॉक आरेख पर वापस जाना होगा और एक ग्यारहवां नियम जोड़ना होगा:
- कई राउंड ब्लॉक और कम से कम एक वर्ग ब्लॉक को उनके चारों ओर एक घेरा बनाकर समूह बनाएं। इसके अलावा जो तीर हो सकता है उसे संलग्न करें। एक बाड़े को बाड़े की सीमा को पार न करने दें अगर वह बाड़े से बाहर / से बाहर नहीं आता है या नहीं जाता है। एक बार यह पूरा हो जाने के बाद, बाड़े के सभी आउटपुट तीरों को देखें। यदि उनमें से कोई भी बाड़े के एक गोल खंड से आता है या बाड़े का एक इनपुट भी है, तो हम इन प्रक्रियाओं को एक तुल्यकालिक प्रक्रिया में विलय नहीं कर सकते हैं। और हम कर सकते हैं।
उदाहरण के लिए, हमारे काउंटर उदाहरण में, हम निम्नलिखित आंकड़े के लाल बाड़े में दो प्रक्रियाओं को समूह नहीं बना सकते हैं:
क्योंकि ncnt
बाड़े का एक आउटपुट है और इसका मूल एक गोल (कॉम्बीनेटरियल) ब्लॉक है। लेकिन हम समूह:
आंतरिक सिग्नल npo
बेकार हो जाएगा और परिणामस्वरूप प्रक्रिया होगी:
poreg: process(clock)
variable tmp: bit;
begin
if rising_edge(clock) then
tmp := '0';
for i in 0 to 8 loop
tmp := tmp xor ncnt(i);
end loop;
po <= not tmp;
end if;
end process poreg;
जिसे अन्य समकालिक प्रक्रिया में भी मिलाया जा सकता है:
reg: process(clock)
variable tmp: bit;
begin
if rising_edge(clock) then
co <= nco;
bo <= nbo;
cnt <= ncnt;
tmp := '0';
for i in 0 to 8 loop
tmp := tmp xor ncnt(i);
end loop;
po <= not tmp;
end if;
end process reg;
समूहीकरण भी हो सकता है:
बहुत सरल वास्तुकला के लिए अग्रणी:
architecture arc5 of cooley is
signal cnt: unsigned(8 downto 0);
begin
process(clock)
variable ncnt: unsigned(9 downto 0);
variable tmp: bit;
begin
if rising_edge(clock) then
ncnt := '0' & cnt;
co <= '0';
bo <= '0';
if up = '0' and down = '0' then
ncnt := unsigned('0' & di);
elsif up = '1' and down = '0' then
ncnt := ncnt + 3;
co <= ncnt(9);
elsif up = '0' and down = '1' then
ncnt := ncnt - 5;
bo <= ncnt(9);
end if;
tmp := '0';
for i in 0 to 8 loop
tmp := tmp xor ncnt(i);
end loop;
po <= not tmp;
cnt <= ncnt(8 downto 0);
end if;
end process;
do <= bit_vector(cnt);
end architecture arc5;
दो प्रक्रियाओं के साथ (के समवर्ती संकेत काम do
बराबर प्रक्रिया के लिए एक आशुलिपि है)। केवल एक प्रक्रिया के साथ समाधान को अभ्यास के रूप में छोड़ दिया जाता है। सावधान, यह दिलचस्प और सूक्ष्म सवाल उठाता है।
इससे भी आगे जा रहे हैं
स्तर-ट्रिगर लैचेज, गिरते हुए घड़ी के किनारों, कई घड़ियों (और क्लॉक डोमेन के बीच रेज़िन सिंक्रोनाइज़र), एक ही सिग्नल के लिए कई ड्राइवर, आदि बुराई नहीं हैं। वे कभी-कभी उपयोगी होते हैं। लेकिन उनका उपयोग कैसे करना है और संबंधित नुकसान से कैसे बचा जाए, यह सीखना VHDL के साथ डिजिटल हार्डवेयर डिज़ाइन के इस संक्षिप्त परिचय से कहीं आगे जाता है।
VHDL 2008 में कोडिंग
VHDL 2008 ने कई संशोधनों को पेश किया जिनका उपयोग हम अपने कोड को और सरल बनाने के लिए कर सकते हैं। इस उदाहरण में हम 2 संशोधनों से लाभ उठा सकते हैं:
- आउटपुट पोर्ट को पढ़ा जा सकता है, हमें किसी भी अधिक
cnt
सिग्नल की आवश्यकता नहीं है, - यूनिटी
xor
ऑपरेटर का उपयोग समता की गणना करने के लिए किया जा सकता है।
VHDL 2008 कोड हो सकता है:
library ieee;
use ieee.numeric_bit.all;
entity cooley is
port(
clock: in bit;
up: in bit;
down: in bit;
di: in bit_vector(8 downto 0);
co: out bit;
bo: out bit;
po: out bit;
do: out bit_vector(8 downto 0)
);
end entity cooley;
architecture arc6 of cooley is
begin
process(clock)
variable ncnt: unsigned(9 downto 0);
begin
if rising_edge(clock) then
ncnt := unsigned('0' & do);
co <= '0';
bo <= '0';
if up = '0' and down = '0' then
ncnt := unsigned('0' & di);
elsif up = '1' and down = '0' then
ncnt := ncnt + 3;
co <= ncnt(9);
elsif up = '0' and down = '1' then
ncnt := ncnt - 5;
bo <= ncnt(9);
end if;
po <= not (xor ncnt(8 downto 0));
do <= bit_vector(ncnt(8 downto 0));
end if;
end process;
end architecture arc6;