- इसमें RV32I शामिल है: रजिस्टर, ABI, और ईकॉल के साथ प्रवाह नियंत्रण।
- बृहस्पति के साथ अभ्यास और अभ्यास: नकारात्मक, कारक, श्रृंखला, पुनरावृत्ति।
- मास्टर क्रॉस टूलचेन, लिपि objdump के साथ लिंकिंग और डिबगिंग.
यदि आप असेंबलर के बारे में उत्सुक हैं और महसूस करते हैं कि RISC-V ही सही रास्ता है, तो आप सही जगह पर आए हैं। RISC-V पर ASM के साथ शुरुआत करना जितना लगता है, उससे कहीं अधिक किफायती है यदि आप उपकरण, मॉडल को समझते हैं प्रोग्रामिंग और वास्तुकला के कुछ प्रमुख नियम।
निम्नलिखित पंक्तियों में मैंने कई स्रोतों से सर्वोत्तम जानकारी को संयोजित किया है: बृहस्पति प्रकार के सिमुलेटर के साथ अभ्यास, RV32I बेस प्रदर्शनों की सूची के सम्मेलनों, लूप और रिकर्सन उदाहरण, सिस्टम कॉल, और यहां तक कि वीएचडीएल (एएलयू, मेमोरी कंट्रोल और स्टेट मशीन के साथ) में आरआईएससी-वी सीपीयू डिजाइन पर एक नज़र, साथ ही क्रॉस-टूलचेन और लिंकिंग स्क्रिप्ट की समीक्षा।
RISC-V असेम्बलर क्या है और यह मशीन भाषा से किस प्रकार भिन्न है?
हालाँकि दोनों एक दूसरे से जुड़े हुए हैं हार्डवेयर, मशीनी भाषा शुद्ध बाइनरी (एक और शून्य) है जिसे सीपीयू सीधे व्याख्या करता है, जबकि असेंबलर स्मृति सहायकों का उपयोग करता है और प्रतीकों असेंबलर की तुलना में अधिक पठनीय है और फिर बाइनरी में अनुवाद करता है।
RISC-V एक बहुत ही स्वच्छ आधार प्रदर्शन के साथ एक खुले ISA को परिभाषित करता है। RV32I (32-बिट) प्रोफ़ाइल में 39 उपयोगकर्ता निर्देश शामिल हैं उल्लेखनीय ऑर्थोगोनलिटी के साथ, शुद्ध गणना से मेमोरी एक्सेस को अलग करना, और जीसीसी/एलएलवीएम में उत्कृष्ट समर्थन के साथ।
रिकॉर्ड, समझौते और प्रवेश बिंदु
RV32I में आपके पास है 32 सामान्य प्रयोजन रजिस्टर (x0–x31) 32-बिट; x0 को हमेशा 0 के रूप में पढ़ा जाता है और इसे लिखा नहीं जा सकता। a0–a7 (आर्ग्युमेंट्स), t0–t6 (अस्थायी), या s0–s11 (सेव्ड) जैसे उपनाम भी ABI का पालन करने के लिए उपयोगी होते हैं।
वातावरण या सिम्युलेटर के आधार पर, प्रोग्राम एक विशिष्ट लेबल पर शुरू हो सकता है। जुपिटर में, प्रोग्राम वैश्विक __start टैग से शुरू होते हैं।, जिसे आपको प्रवेश बिंदु को चिह्नित करने के लिए दृश्यमान घोषित करना होगा (उदाहरण के लिए, .globl के साथ)।
लास टैग कोलन में समाप्त होते हैं, आप प्रति पंक्ति केवल एक निर्देश डाल सकते हैं और टिप्पणियाँ # या ; से शुरू की जा सकती हैं, इसलिए असेंबलर उन्हें अनदेखा कर देता है।
उपकरण और सिमुलेटर: जुपिटर और बुनियादी वर्कफ़्लो
जटिलताओं के बिना अभ्यास करने के लिए, आपके पास है बृहस्पति सिम्युलेटर/असेम्बलर, SPIM/MARS/VENUS से प्रेरित एक ग्राफिकल टूल जो एकल वातावरण में संपादन, संयोजन और निष्पादन की सुविधा प्रदान करता है।
जुपिटर में आप एडिटर टैब में फ़ाइलें बना सकते हैं, संपादित कर सकते हैं और हटा सकते हैं। सेव करने के बाद, F3 के साथ असेंबल करें और चलाएँ मशीन की स्थिति को समझने के लिए रजिस्टर और मेमोरी दृश्यों का उपयोग करते हुए, निर्देश दर निर्देश प्रवाह को डीबग करना।
कार्यक्रम का अंत पर्यावरण के आह्वान के साथ होना चाहिए: कोड 10 के साथ कॉल सेटिंग a0 से बाहर निकलें (बाहर निकलें)। RISC-V में, ई-कॉल, पर्यावरण/सिस्टम के लिए सिस्टम कॉल या ट्रैप के समतुल्य हैं।
प्रोग्राम और सिस्टम कॉल की न्यूनतम संरचना
अकादमिक उदाहरणों में विशिष्ट संरचना एक प्रारंभिक बिंदु को परिभाषित करती है, कार्य करती है, और एक ई-कॉल के साथ समाप्त होती है। ईकॉल तर्क आमतौर पर a0–a2 में यात्रा करते हैं और a7 में सेवा चयनकर्ता, पर्यावरण पर निर्भर करता है।
एक में Linux उदाहरण के लिए, RISC-V में आप write syscall के साथ प्रिंट कर सकते हैं और उपयुक्त कोड के साथ बाहर निकल सकते हैं। लिखने के लिए a0 (fd), a1 (बफर), a2 (लंबाई) और a7 का उपयोग सेवा संख्या के साथ किया जाता हैअंत में, a0 को रिटर्न कोड और a7 को एग्जिट नंबर पर सेट किया जाता है।
# Ejemplo mínimo (Linux RISC-V) para escribir y salir
.global _start
_start:
addi a0, x0, 1 # fd = 1 (stdout)
la a1, msg # a1 = &msg
addi a2, x0, 12 # a2 = longitud
addi a7, x0, 64 # write
ecall
addi a0, x0, 0 # return code
addi a7, x0, 93 # exit
ecall
.data
msg: .ascii "Hola mundo\n"
यदि आप लिनक्स के बाहर काम करते हैं, जैसे कि अपनी स्वयं की IoT सेवा के साथ शैक्षिक सिमुलेटर, पर्यावरण दस्तावेज़ के अनुसार ईकॉल नंबर और रजिस्टर बदलें।
प्रारंभिक अभ्यास जो आपको सशर्त, लूप और मेमोरी के साथ सहज होने में मदद करेंगे
एक सामान्य वार्म-अप यह पता लगाने के लिए होता है कि कोई पूर्णांक ऋणात्मक है या नहीं। यदि यह धनात्मक है तो आप 0 लौटा सकते हैं और यदि यह ऋणात्मक है तो 1 लौटा सकते हैं।; RV32I के साथ, 0 और सेट-ऑन-लेस-दैन के साथ तुलना, एक सुविचारित निर्देश में समस्या का समाधान कर देती है।
एक और बहुत उपयोगी अभ्यास है किसी संख्या के गुणनखंडों को सूचीबद्ध करना: 1 से n तक जाता है, विभाजकों को प्रिंट करता है, और बताता है कि कितने थेआप सशर्त शाखाओं, विभाजन (या बार-बार घटाव) और जोड़ और तुलना के साथ लूप का अभ्यास करेंगे।
स्ट्रिंग्स के साथ काम करने से आपको मेमोरी का प्रबंधन करना पड़ता है: मेमोरी में स्ट्रिंग के प्रत्येक अक्षर पर जाएँ और जगह-जगह लोअरकेस को अपरकेस में परिवर्तित करें अगर वे ASCII रेंज में फिट होते हैं, तो यह स्ट्रिंग का मूल पता लौटाता है।
लूप्स, फंक्शन्स और रिकर्सन: फैक्टोरियल, फिबोनाची और टॉवर ऑफ हनोई
लूप्स डिजाइन करते समय, तीन ब्लॉकों के बारे में सोचें: कंडीशन, बॉडी और स्टेप। beq/bne/bge और बिना शर्त जंप के साथ jal/j while/for का निर्माण किया जाता है रहस्य के बिना, addi और तुलना पर निर्भर.
.text
.globl __start
__start:
li t0, 0 # i
li t1, 10 # max
cond:
bge t0, t1, end # si i >= max, salta
# cuerpo: usar a0/a1 segun servicio IO del entorno
addi t0, t0, 1 # i++
j cond
end:
li a0, 10
ecall
फ़ंक्शन कॉल में, ABI का सम्मान करें: यदि आप अधिक कॉल श्रृंखलाबद्ध करने जा रहे हैं तो बचत करें, यदि आप उन्हें संशोधित करते हैं तो s0–s11 को संरक्षित करता है, और एक शब्द के गुणकों में sp के साथ स्टैक का उपयोग करता है।
फैक्टोरियल क्लासिक पुनरावर्तन है: आधार स्थिति n==0 1 लौटाता है; अन्यथा, फैक्टोरियल(n-1) को कॉल करें और n से गुणा करें। कॉल से पहले स्टैक पर सहेजे गए ra और रजिस्टरों को सुरक्षित करें और वापस आने पर उन्हें पुनर्स्थापित करें।
factorial:
beq a0, x0, base
addi sp, sp, -8
sw ra, 4(sp)
sw s0, 0(sp)
mv s0, a0
addi a0, a0, -1
jal factorial
mul a0, a0, s0
lw s0, 0(sp)
lw ra, 4(sp)
addi sp, sp, 8
jr ra
base:
li a0, 1
jr ra
फिबोनाची दोनों का अभ्यास करने के लिए उपयोगी है दो कॉल के साथ पुनरावृत्ति संचायक चरों के साथ एक कुशल पुनरावृत्त संस्करण के रूप में। और यदि आप प्रवाह और पैरामीटर नियंत्रण चुनौती चाहते हैं, तो समाधान को असेंबलर में अनुवादित करें। हनोई के टावर चार तर्कों के साथ: डिस्क, स्रोत, गंतव्य और सहायक टॉवर; यह कॉल ऑर्डर का सम्मान करता है और प्रत्येक मूवमेंट को प्रदर्शित करता है।
मेमोरी एक्सेस, एरे और स्ट्रिंग मैनिपुलेशन
RISC-V में, मेमोरी एक्सेस लोड/स्टोर के साथ किया जाता है: शब्दों के लिए lw/sw, आधे शब्दों के लिए lh/sh, और बाइट्स के लिए lb/sb, शुल्कों में हस्ताक्षरित या अहस्ताक्षरित रूपों के साथ (lb बनाम lbu, lh बनाम lhu)।
पूर्णांक सरणियों को पार करने के लिए, प्रति इंडेक्स 4-बाइट ऑफसेट का उपयोग करें; पाठ स्ट्रिंग के लिए, बाइट दर बाइट आगे बढ़ता है जब तक कि उसे टर्मिनेटर न मिल जाए यदि कन्वेंशन की आवश्यकता हो (उदाहरण के लिए, \0)। आधार पतों को सहेजना और पॉइंटर्स को आवश्यकतानुसार addi/auipc/la से संभालना याद रखें।
एक RV32I CPU को शुरू से डिज़ाइन करना: एक उच्च-स्तरीय अवलोकन
यदि आप सिलिकॉन की ओर जाना चाहते हैं, तो एक शैक्षिक परियोजना एक रास्ता बनाती है VHDL में RV32I CPU, FPGA में संश्लेषण योग्य निम्न-मध्य श्रेणी। इसमें प्रोग्राम ROM, डेटा RAM, और LED जलाने के लिए एक सरल GPIO शामिल है।
कर्नेल आधार प्रदर्शनों की सूची को लागू करता है (एम/ए/सी एक्सटेंशन या सीएसआर के बिना), 32-बिट एड्रेस बस का उपयोग करता है और जहाँ उपयुक्त हो, 8-/16-/32-बिट साइन-एक्सटेंडेड मेमोरी एक्सेस की अनुमति देता है। यह डिज़ाइन रजिस्टरों, ALU, मेमोरी कंट्रोलर और स्टेट मशीन को स्पष्ट रूप से अलग करता है।
ALU, शिफ्ट और "विलंबित लोडिंग" का विचार
ALU को निम्नलिखित परिचालनों के साथ संयोजनात्मक रूप से वर्णित किया गया है जोड़, घटाव, XOR, OR, AND, तुलना (हस्ताक्षरित और अहस्ताक्षरित) और तार्किक/अंकगणितीय बदलाव।
FPGA में LUTs को बचाने के लिए, मल्टी-बिट शिफ्ट लागू किए जाते हैं स्टेट मशीन द्वारा नियंत्रित 1-बिट शिफ्टों की पुनरावृत्ति: आप कई चक्रों का उपभोग करते हैं, लेकिन आप तार्किक संसाधनों को कम करते हैं।
तुल्यकालिक सर्किट में, घड़ी के किनारों पर परिवर्तन देखे जाते हैं। "विलंबित लोड" की अवधारणा हमें याद दिलाती है कि मल्टीप्लेक्सर द्वारा जो भी चुना जाता है, उसका प्रभाव अगले चक्र में रजिस्टर पर पड़ता है।, फ़ेच-डिकोड-एक्सिक्यूट स्टेट मशीन को डिज़ाइन करते समय एक महत्वपूर्ण पहलू।
मेमोरी नियंत्रक और मानचित्र: ROM, RAM और GPIO
एक मेमोरी ब्लॉक ROM और RAM को एक सन्निहित स्थान में एकीकृत करता है, प्रोसेसर इंटरफ़ेस को सरल बनानानियंत्रक लेनदेन आरंभ करने के लिए AddressIn (32 बिट), DataIn, चौड़ाई (बाइट/आधा/शब्द), साइन एक्सटेंशन सिग्नल, WE (पढ़ें/लिखें) और Start प्राप्त करता है।
जब ऑपरेशन ख़त्म हो जाता है, रेडीआउट 1 पर सेट है और, यदि इसे पढ़ा गया हैDataOut में डेटा होता है (अनुरोध किए जाने पर साइन-एक्सटेंडेड)। अगर इसे लिखा गया था, तो डेटा RAM में ही रहता है।
व्यावहारिक मानचित्र का एक उदाहरण: ROM 0x0000 से 0x0FFF तक, 0x1000 पर एक GPIO बाइट (पिन के लिए बिट 0) और RAM 0x1001 से 0x1FFF तकइससे आप आउटपुट बिट लिखकर और टॉगल करके ब्लिंकर बना सकते हैं।
रजिस्टर, मल्टीप्लेक्सर और स्टेट मशीन
सीपीयू 32 सामान्य प्रयोजन रजिस्टरों को परिभाषित करता है, जो वीएचडीएल में सरणियों के साथ त्वरित होते हैं, विकोडक ALU से लेखन गंतव्य का चयन करें और बाकी को रखें।
मल्टीप्लेक्सर्स ALU इनपुट (ऑपरेंड और ऑपरेशन) को नियंत्रित करते हैं, मेमोरी कंट्रोलर को सिग्नल (चौड़ाई, पता, प्रारंभ नियंत्रण और पढ़ना/लिखना) और विशेष रजिस्टर: पीसी, आईआर और पुनरावृत्तीय शिफ्ट के लिए एक सहायक काउंटर।
राज्य मशीन से शुरू होता है रीसेट करें, पीसी द्वारा इंगित निर्देश प्राप्त करता है (4-बाइट रीडिंग), यह तैयार होने पर IR में लोड किया जाता है और निष्पादन नोड्स को पास करता है: ALU (शिफ्ट को छोड़कर 1 चक्र में एक निर्देश), लोड/स्टोर, शाखाएं और जंप, इसके अलावा विशेष निर्देश जैसे कि ebreak।
क्रॉस-टूलचेन, लिंकिंग और डिबगिंग
RV32I बाइनरी उत्पन्न करने के लिए, का उपयोग करें क्रॉस GCC (लक्ष्य riscv32-none-elf)आप C/C++/ASM स्रोतों को संकलित करते हैं, एक स्क्रिप्ट के साथ लिंक करते हैं जो मेमोरी मैप को परिभाषित करता है, और आउटपुट को उस रूप में परिवर्तित करते हैं जो आपका ROM/FPGA अपेक्षा करता है।
एक सरल हुक स्क्रिप्ट जगह कर सकते हैं 0x0000 से ROM में .text और .data को 0x1004 से RAM में (यदि 0x1000–0x1003 GPIO रजिस्टरों द्वारा व्याप्त है)। स्टार्टअप रूटीन को "नग्न" किया जा सकता है और RAM के अंत में स्टैक पॉइंटर (उदाहरण के लिए 0x1FFC) मुख्य कॉल करने से पहले.
/* Mapa simple
* ROM: 0x00000000 - 0x00000FFF
* GPIO: 0x00001000 - 0x00001003
* RAM: 0x00001004 - 0x00001FFF
*/
SECTIONS {
. = 0x00000000;
.text : { *(.startup) *(.text) *(.text.*) *(.rodata*) }
. = 0x00001004;
.data : { *(.data) *(.data.*) }
}
riscv32-none-elf-objdump के साथ आप कर सकते हैं ELF को अलग करें और पते जांचें; उदाहरण के लिए, आप देखेंगे बूट 0x00000000 पर lui/addi/jal जैसे निर्देशों और आपके मुख्य सर्वर में संक्रमण के साथ। VHDL सिमुलेशन के लिए, GHDL ट्रेस उत्पन्न करता है जिन्हें आप GtkWave के साथ खोल सकते हैं।
सिमुलेशन में सत्यापन के बाद, डिज़ाइन को FPGA (क्वार्टस या अन्य टूलचेन) पर ले जाएं। यदि RAM को आंतरिक ब्लॉक के रूप में अनुमानित किया जाता है और कोड स्पष्ट RTL है, आपको अनुभवी उपकरणों पर भी, बिना किसी आश्चर्य के संश्लेषण करना चाहिए।
शुरुआत करते समय व्यावहारिक अनुस्मारक और सामान्य गलतियाँ
मत भूलना x0 हमेशा शून्य होता है; इसमें लिखने पर कोई प्रभाव नहीं पड़ता, तथा इसे पढ़ने पर 0 लौटता है। इसे जोड़ने, तुलना करने, तथा रजिस्ट्री क्लीनअप में अपने लाभ के लिए उपयोग करें।
जब आप सुविधाएँ क्रियान्वित करते हैं, आपके द्वारा संशोधित ra और sN रिकॉर्ड सहेजें, और sp में शब्द-संरेखित जोड़/घटाव के साथ स्टैक का प्रबंधन करता है। वापस लौटने पर, यह उल्टे क्रम में पुनर्स्थापित होता है और jr ra के साथ कूदता है।
जुपिटर जैसे सिमुलेटर में, जाँच करें कि __start वैश्विक है और आप इसे ecall के साथ समाप्त करते हैं सही (बाहर निकलने के लिए a0=10)। अगर कुछ शुरू नहीं होता है, तो लेबल, ग्लोबलिटी की जाँच करें और पुनः संकलित करें (F3)।
IO के साथ अभ्यास में, पर्यावरण के प्रोटोकॉल का सम्मान करें: कौन से रजिस्टर पैरामीटर ले जाते हैं, सेवा संख्या, और क्या कोई पता या तत्काल मान अपेक्षित है। सिम्युलेटर या ऑपरेटिंग सिस्टम दस्तावेज़ का उपयोग करें।
एक स्पष्ट ISA आधार (RV32I, रजिस्टर और ABI), जुपिटर जैसे आरामदायक सिम्युलेटर, और बढ़ते उदाहरणों (ऋणात्मक, कारक, अपरकेस, लूप, फैक्टोरियल, फिबोनाची और हनोई) के साथ, RISC-V असेंबलर एक दीवार बनना बंद कर देता है और यह समझने के लिए एक रोचक क्षेत्र बन जाता है कि CPU कैसे सोचता है। और यदि आप VHDL तक जाने का साहस करते हैं, तो आप देखेंगे कि ALU, मेमोरी और नियंत्रण एक साथ कैसे फिट होते हैं।: निर्देश प्राप्ति और आलसी लोडिंग से लेकर मेमोरी इंटरफेस और ROM, RAM और GPIO के साथ एक मानचित्र जो आपको अपने स्वयं के प्रोसेसर के साथ एक एलईडी को ब्लिंक करने की सुविधा देता है।
सामान्य तौर पर बाइट्स और प्रौद्योगिकी की दुनिया के बारे में भावुक लेखक। मुझे लेखन के माध्यम से अपना ज्ञान साझा करना पसंद है, और यही मैं इस ब्लॉग में करूंगा, आपको गैजेट्स, सॉफ्टवेयर, हार्डवेयर, तकनीकी रुझान और बहुत कुछ के बारे में सबसे दिलचस्प चीजें दिखाऊंगा। मेरा लक्ष्य आपको डिजिटल दुनिया को सरल और मनोरंजक तरीके से नेविगेट करने में मदद करना है।