
SWD डिबग प्रोब लाइब्रेरी RP2350 RISC-V कोर के लिए। PIO बिट-बैंगिंग का उपयोग करके दो GPIO तारों पर halt/resume/step, रजिस्टर और मेमोरी एक्सेस, कोड निष्पादन और इंस्ट्रक्शन ट्रेसिंग प्रदान करता है।
SWD डीबग प्रोब RP2350 RISC-V (Hazard3) कोर के लिए। एक Pico2 दूसरे को दो GPIO तारों के माध्यम से डीबग करता है।
लगभग 80% कोड वाइब कोडेड है; रीडमे लगभग पूरी तरह से जनरेट किया गया है (संपूर्ण वाइब-कोड-चेतावनी अनुभाग को छोड़कर)। मैंने ऑसिलोस्कोप और डॉक्स के साथ कई रातें बिताईं और एक कार्यशील प्रोटोटाइप बनाया जो sba/read/write regs और abstract commands तथा progbuf करने में सक्षम था, बाकी क्लाउड कोड से किया गया। परीक्षण काफी व्यापक परीक्षण सूट हैं और मैं अपने स्वयं के प्रोजेक्ट्स में लाइब्रेरी के कोर का उपयोग करता हूं, लेकिन, जैसा कि वे कहते हैं, "यहाँ ड्रेगन हैं"। मैंने रीडमे और कोड भी पढ़ा और कुछ भी गलत नहीं देखा (और गलत/अस्पष्ट भागों को हटा दिया)।
यह प्रोजेक्ट एक अधिक जटिल प्रोजेक्ट को विबकोड करने का मेरा केसस्टडी था जिसे मैं 100% नहीं समझता और जिसके लिए कोई स्पष्ट मौजूदा कोड नहीं है जिसे "उपयोग" किया जा सके। यह लगभग 1000 लाइनों से शुरू हुआ जो मैंने स्वयं लिखा था और अच्छी तरह जानता था, rp2350, आर्म swd और रिस्क-वी डीबग डॉक्स पढ़ते हुए, ऑसिलोस्कोप और ओपनोसीडी के साथ डेटा कैप्चर करते हुए, फिर उसे डीकोड करते हुए और वेकअप अनुक्रम तथा फिर रीड/राइट कमांड्स का विश्लेषण करते हुए। जब मैंने इसे काम करने लगा तो मैंने इसे क्लाउड को दिया ताकि इसे एक लाइब्रेरी में बदल दिया जाए जिसका उपयोग मैं अन्य प्रोजेक्ट्स में कर सकूं, और फिर मैंने धीरे-धीरे इसे बनाया।
लगभग 3-4k लाइनों के कोड के बाद मैंने पूरी तरह से ट्रैक खो दिया कि क्या हो रहा है, और मैं इस कोड को अपना लिखा हुआ नहीं मानूंगा, लेकिन अधिक से अधिक परीक्षण जोड़ना "अच्छा" लगा, या कम से कम आश्वस्त करने वाला।
कुछ गैसलाइटिंग थी, विशेष रूप से जब इसने dap_read_mem32 को गलत समझा कि यह रैम से पढ़ रहा है न कि MEM-AP TAR/DRW/RDBUFF प्रोटोकॉल से, जिससे अविश्वसनीय मात्रा में बकवास हुई।
कुल मिलाकर मैं कहूंगा कि यह एक भयानक अनुभव था, भले ही लगभग 10000 लाइनों का कोड लिखने में 10 घंटे लग गए, मैं इस प्रोजेक्ट को अपना नहीं मानता, और मुझे उपलब्धि या विकास की कोई भावना नहीं है।
इसके विपरीत, सभी डॉक्स (जो हजारों पृष्ठ हैं) को पढ़ने और ऑसिलोस्कोप डेटा को डीकोड करने, डॉक्स से पैक किए गए C स्ट्रक्ट्स बनाने आदि के लिए सहायक स्क्रिप्ट लिखने के लिए AI का उपयोग करना बहुत अच्छा था, और मुझे इसके बाद अच्छा लगा। जिस क्षण मैंने पहला रजिस्टर पढ़ा और फिर जब मैं SBA के माध्यम से मेमोरी पढ़ने में सक्षम हुआ तो मुझे अद्भुत महसूस हुआ।
मुख्य मुद्दा स्वाद है, जब मैं कोड लिखता हूं तो मुझे लगता है कि यह अच्छा है या बुरा, जैसे ही मैं लिख रहा होता हूं, मुझे पता होता है कि यह गलत है, लेकिन क्लाउड कोड का उपयोग करके मैं बहुत जल्दी असंवेदनशील हो जाता हूं और मैं बस नहीं बता सकता, यह "ठीक" लगता है, लेकिन मुझे नहीं पता कि यह कैसा लगता है। इस मामले में यह तब हुआ जब कोड लगभग 4x बढ़ गया, 1k से 4k लाइनों तक। और सबसे बुरी बात, कोड का मेरा मानसिक मॉडल पूरी तरह से गायब है, और उसके साथ मेरा स्वामित्व भी।
टोकन का कोई कारण या उद्देश्य नहीं है, जो कोड पढ़ने को बेहद कठिन बना देता है, क्योंकि प्रत्येक टोकन पूरी तरह से बकवास हो सकता है। मानव कोड पढ़ते समय प्रतीकों का एक उद्देश्य होता है, किसी ने सोचा "मैं इसे एक वेरिएबल में डालूंगा, बाद में मैं इसकी स्थिति जांचूंगा।" इसलिए मैं उन्हीं की तरह दिखावा करता हूं, और सोचता हूं कि उन्होंने ऐसा क्यों लिखा? थोड़ी देर बाद मैं समझ जाता हूं, क्योंकि वे मानव हैं और मैं मानव हूं। लेकिन AI प्रतीकों का कोई कारण नहीं है, और सबसे बुरी बात, वे सभी भ्रामक रूप से सही दिखते हैं, इसलिए मुझे यह सोचने में 10 गुना अधिक मेहनत करनी पड़ती है कि क्या यह गलत है। किसी भी मानव कोड (अपने स्वयं के कोड सहित) के साथ यह अनुमान लगाना काफी आसान है कि आप उस पर कितना भरोसा कर सकते हैं, और यह काफी सुसंगत है, जबकि AI कोड के साथ, एक फंक्शन उससे कहीं बेहतर हो सकता है जो आपने लिखा होता, और उससे 2 लाइन नीचे का कोड कार्गो कल्टेड गंक हो सकता है जो अविश्वसनीय रूप से अच्छा दिखता है, लेकिन संरचनात्मक रूप से गलत है।
अंत में मैं कहूंगा कि मैंने तारों, समय, और निचले स्तर की एपी/डीपी यांत्रिकी, एसबीए और प्रोगबफ की अच्छी समझ प्राप्त की है, लेकिन मुझे खेद है कि मैंने पूरी चीज खुद नहीं लिखी, भले ही इसमें 10 गुना अधिक समय लगता।
मुझे इससे नफरत है।
और मैं मदद नहीं कर सकता, लेकिन घृणा और शर्म महसूस करता हूं। क्या अब प्रोग्रामिंग यही है? मैं वास्तव में आशा करता हूं कि यह कोई मध्यवर्ती चरण है और यह बेहतर के लिए बदलता है, समस्या यह है कि मुझे नहीं पता कि "बेहतर" क्या है, कुछ लोगों के लिए कोड न लिखना है, दूसरों के लिए समस्या का मॉडल न बनाना है और तीसरे के लिए सोचना नहीं है। मेरे लिए, मुझे यकीन नहीं है, मैं चीजें बनाना चाहता हूं, और कई बार मैं कुछ नहीं जानना चाहता, लेकिन मैं इसका उपयोग करना चाहता हूं, उदाहरण के लिए rp2350 usb होस्ट कंट्रोलर जिस तरह से आपको इंटरप्ट्स को री-आर्म करना होता है और जिस तरह से epx रजिस्टर साझा किया जाता है वह बहुत कष्टप्रद है, शायद अच्छे कारणों से, लेकिन मैं बस इसका उपयोग अपना CBI ड्राइवर बनाने के लिए करना चाहता हूं।
मुझे लगता है कि सवाल यह है कि मैं क्या बनाना चाहता हूं, क्योंकि आप स्टैक में बहुत ऊपर जा सकते हैं, USB चिप रजिस्टर से लेकर CBI से UFI से FAT16 से उस पुराने स्कूल कंप्यूटर के OS तक जो मैं बना रहा हूं, लेकिन क्यों रुकें? स्कीमैटिक्स, पीसीबी, कैड फाइलें बनाएं, शायद इसे ऑटोमैटिक कारखाने को भेजें? और फिर इसे मेरे पास भेजें? लेकिन क्यों रुकें? मेरा वेबशॉप बनाएं, बेचना शुरू करें, एक समुदाय बनाएं, विज्ञापन, मार्केटिंग, कुछ अनबॉक्सिंग वीडियो जनरेट करें, शायद कुछ वायरल मीम्स? ऑर्डर को सीधे कारखाने में प्रोसेस करें, ऑन डिमांड, अगर कोई समस्या है, तो यह ग्राहक सहायता के साथ तैयार है।
इस बीच मैं क्या करूं? समुद्र तट पर बैठूं? मुझे समुद्र तट से नफरत है।
यह कहाँ रुकता है?
PS: जैसा कि कवि ने कहा: जो आप चाहते हैं उसे पाने की कीमत वह है जो आप एक बार चाहते थे।
Application
|
rp2350.c RISC-V Debug Module (halt/resume/step, registers, memory, trace)
|
dap.c Debug Access Port (DP/AP registers, bank caching, MEM-AP)
|
swd_protocol.c SWD wire protocol (PIO bit-banging, packet encoding, retry)
|
swd.pio PIO state machine (4-cycle SWCLK, bidirectional SWDIO)
प्रत्येक परत अपनी खुद की स्थिति बनाए रखती है और केवल अपने पड़ोसी से बोलती है।
swd_config_t config = swd_config_default();
config.pin_swclk = 2;
config.pin_swdio = 3;
swd_target_t *target = swd_target_create(&config);
swd_connect(target);
rp2350_init(target);
rp2350_halt(target, 0);
swd_result_t pc = rp2350_read_pc(target, 0);
rp2350_resume(target, 0);
swd_target_destroy(target);
rp2350_halt(target, 0);
rp2350_step(target, 0);
rp2350_resume(target, 0);
rp2350_reset(target, 0, true);
swd_result_t pc = rp2350_read_pc(target, 0);
rp2350_write_pc(target, 0, 0x20000000);
swd_result_t val = rp2350_read_reg(target, 0, 5);
rp2350_write_reg(target, 0, 5, 0xDEADBEEF);
uint32_t regs[32];
rp2350_read_all_regs(target, 0, regs);
swd_result_t csr = rp2350_read_csr(target, 0, 0x300);
rp2350_write_csr(target, 0, 0x300, value);
दोनों हार्ट (0 और 1) स्वतंत्र रूप से नियंत्रणीय हैं।
सिस्टम बस एक्सेस के माध्यम से गैर-आक्रामक। हार्ट चलने के दौरान काम करता है।
swd_result_t val = rp2350_read_mem32(target, 0x20000000);
rp2350_write_mem32(target, 0x20000000, 0xDEADBEEF);
rp2350_read_mem16(target, addr);
rp2350_write_mem8(target, addr, byte);
uint32_t buf[256];
rp2350_read_mem_block(target, 0x20000000, buf, 256);
rp2350_write_mem_block(target, 0x20000000, buf, 256);
ब्लॉक ट्रांसफर प्रदर्शन के लिए SBA ऑटो-इंक्रीमेंट का उपयोग करते हैं।
const uint32_t program[] = {
0x200415b7, // lui a1, 0x20040
0xabcd0537, // lui a0, 0xabcd0
0x00a5a223, // sw a0, 4(a1)
0x0000006f, // j . (loop)
};
rp2350_execute_code(target, 0, 0x20000000, program, 4);
लक्ष्य SRAM पर अपलोड करता है, सत्यापित करता है, PC सेट करता है, फिर से शुरू करता है।
bool on_instruction(const trace_record_t *rec, void *ctx) {
printf("0x%08x: 0x%08x\n", rec->pc, rec->instruction);
return true;
}
int traced = rp2350_trace(target, 0, 100, on_instruction, NULL, false);
DCSR.step के माध्यम से निर्देशों पर एकल-चरण। ~5ms प्रति निर्देश रजिस्टर कैप्चर के बिना, ~80ms पूर्ण रजिस्टर कैप्चर के साथ।
डीबग संदर्भ में प्रत्यक्ष RISC-V निर्देश निष्पादन:
uint32_t progbuf[] = {
0x34202473, // csrr s0, mcause
0x00100073 // ebreak
};
rp2350_execute_progbuf(target, 0, progbuf, 2);
swd_result_t mcause = rp2350_read_reg(target, 0, 8);
CSR एक्सेस आंतरिक रूप से इसका उपयोग करता है क्योंकि Hazard3 अमूर्त CSR कमांड का समर्थन नहीं करता है।
add_subdirectory(lib/pico2-swd-riscv)
target_link_libraries(your_app pico2_swd_riscv)
डीबग वर्बोसिटी (संकलन-समय):
set(PICO2_SWD_DEBUG_LEVEL 3) # 0=none, 1=warn, 2=info, 3=debug
SWD 8-बिट अनुरोध पैकेट (start, APnDP, RnW, addr[3:2], parity, stop, park), 3-बिट ACK (OK=1, WAIT=2, FAULT=4), और पैरिटी के साथ 33-बिट डेटा चरणों का उपयोग करता है। टर्नअराउंड चक्र SWDIO दिशा परिवर्तन को संभालते हैं। WAIT प्रतिक्रियाएं स्वचालित रूप से पुनः प्रयास करती हैं (डिफ़ॉल्ट: 5 पुनः प्रयास, 100us बैकऑफ़)।
गैर-मानक: [15:12]=APSEL, [11:8]=0xD, [7:4]=bank, [0]=ctrlsel। बिट्स [11:8] में 0xD आवश्यक है लेकिन अप्रलेखित है।
बैंक 1 CSW के माध्यम से तीन-चरणीय हैंडशेक: निष्क्रिय (0x00000000), सक्रिय (0x00000001), पूर्ण कॉन्फ़िग (0x07FFFFC1)। अपेक्षित स्थिति प्रतिक्रिया: 0x04010001।
अमूर्त कमांड के माध्यम से GPRs (regno 0x1000+n, 32-बिट ट्रांसफर)। प्रोग्राम बफर के माध्यम से CSRs: s0 सहेजें, csrr s0, <csr> या csrw <csr>, s0 निष्पादित करें, s0 पढ़ें/पुनर्स्थापित करें।
SBCS को sbaccess=32bit और sbreadonaddr के साथ कॉन्फ़िगर किया गया। SBADDRESS0 लिखना बस रीड को ट्रिगर करता है; डेटा तुरंत SBDATA0 में उपलब्ध। ब्लॉक ट्रांसफर sbautoincrement को सक्षम करते हैं जो प्रति-शब्द पता सेटअप के बिना स्ट्रीमिंग रीड/राइट के लिए है।
प्रोगबफ के माध्यम से DCSR पढ़ें, स्टेप बिट (बिट 2) सेट करें, हार्ट को फिर से शुरू करें। हार्ट एक निर्देश निष्पादित करता है और फिर से डीबग मोड में प्रवेश करता है। स्टेप बिट साफ़ करें।
MIT. देखें LICENSE.