Povestea unui proiect

De cînd mă știu am încercat să înțeleg cum funcționează chestiile cu care interacționez, indiferent că sînt mecanice, electronice, oameni sau gînduri. N-am idee dacă m-am născut curios sau dacă am devenit pe parcurs, cert e că în timp mi-am dezvoltat o artă formidabilă de a sări cu capul înainte în necunoscut. Ce, n-a mai făcut nimeni asta pînă acum? Dă-ncoa’! Nu știm sigur cum ar trebui să funcționeze, dar… Dă-ncoa’, n-auzi?! Încă nu am găsit un motiv suficient de bun pentru a mă împiedica să-mi vîr coada în te miri ce. Cert e că am ajuns să știu cîte puțin din multe și nimic în detaliu.

Ca inginer sînt jalnic, dar mă mîndresc cu faptul că din toate proiectele ajunse vreodată la mine nu a plecat nici măcar unul fără rezolvare. Interesant, provocator, necunoscut. Iată trei cuvinte care descriu perfect lucrurile care mă incită. Nu-mi pot imagina cum ar arăta viaţa fără probleme. Banal, pasiv, rutină. Bagă-mi astea în muncă și te alegi cu o demisie.

Ingineria e arta transformării imposibilului în posibil prin aproximări și presupuneri. Toate constantele sînt variabile, toți parametrii necesari sînt necunoscuți, iar cei cunoscuți nu sînt necesari. Teoretic sînt inginer, practic sînt un lache pîrlit care aplică metoda bîjbîielilor succesive pînă rezolvă problema sau problemele de pe cap. La fel funcţionez – într-o ordine aleatoare – şi în calitate de soț, tată, prieten, om.


Septembrie 2015 – „Vreau eu!”

Se anunță un proiect complet nou, bazat pe o arhitectură diferită faţă de cele deja existente. Anul fusese extrem de plictisitor și voiam ceva mai complex, să-mi trezească neuronul. Avem ECU? Nu, doar o placă de evaluare. Bine, dă-ncoa’. E vreo software început? Avem un „hello_world” care nu merge. Eh, nu prea le am, dar dă-ncoa’, mă ocup io. Încep să adun informații legate de proiect, extrem de relaxat. Mi se spune că ar fi excelent să avem un mod de comunicare şi posibilitatea citirii/scrierii tuturor regiştrilor şi/sau zonelor de memorie pînă în martie 2016, cînd proiectul urma să fie preluat de cei care se ocupă de implementat divere low-level.

Încep să se contureze niște cerințe legate de funcționalitate. Proiectul pare un pic mai dificil față de ce așteptam eu, dar nu mă îngrijorez. Toată luna adun informații legate de compilator, instalez aplicațiile necesare și încerc să fac rost de un exemplu de cod funcţional. La sfîrșitul lunii îmi iese prima compilare şi primul binar care rula corect.


Octombrie 2015 – „Nu e aşa simplu cum credeam…”

Mă chinui să fac faţă curbei imense de învăţare de care am parte:

  • noul meu compilator e practic o linie de comandă, iar proiectul trebuie configurat ad-labam, cu start-up în assembler, linker şi makefile;
  • placa de dezvoltare se configurează cu vreo 200 de jumperi, deci multiple posibilităţi de a da gherle;
  • în funcţie de ce nucleu foloseam, trebuia să schimb configuraţia jumperilor şi modul de formatare a cardului SD;
  • foaia de catalog are 4907 pagini, plus 5 XLS-uri ataşate.

Habar n-aveam cum să scriu codul ca să funcţioneze pe toate nucleele. De fapt, nici nu ştiam dacă trebuie doar pentru unul sau pentru toate. A53-ul era mai nesimţit, se baza pe un kernel de linux din care lansa aplicaţia, iar pentru asta trebuia să fac două partiţii pe card: un EXT3 pentru kernel şi un NTFS pentru aplicaţie. Nu a fost mare problemă, fiind utilizator de openSUSE de ceva ani, aşa că mi-am tras un VirtualBox, am instalat o imagine şi-am tras un script în bash cu comenzile de partiţionat, formatat şi copiat imaginile, să nu le mai tot scriu de mînă. Trebuia să fac asta după fiecare compilare, adică o grămadă de timp pierdut. Pînă la urmă am ales să scriu cod doar pentru nucleul de Cortex-M4. Era mult mai rapid de lucrat cu el: transferat binarul din PC pe SD card, înfipt cardul în placă, reset şi gata. În plus, aveam un cod de început mult mai bun decît cel pentru Cortex-A53.

E simplu să compilezi un program care nu face absolut nimic, modificînd codul scris de altul, dar eu aș vrea niște comunicație. Uite aşa începe jalea, fiindcă prăpăditele mele de Atmeluri pe 8 biţi nu au nici cea mai mică legătură cu nenorocirea din faţa mea: un penta-core pe 32 de biţi, Cortex-M4 şi quad-core Cortex-A53. Mă uitam prin diagrama internă ca mîţa la drojdie; habar nu aveam ce e acolo. În afară de asta, documentaţia e scrisă extrem de stupid şi informaţiile relevante despre funcţionalitatea regiştrilor fiind împrăştiate peste tot, făcînd salturi de cîteva zeci de pagini. Am avut momente în care stăteam cu acelaşi PDF deschis în Adobe, Foxit şi Mozilla ca să nu mai tot sar de la o pagină la alta.


Noiembrie 2015 – „În ce dracului m-am băgat?!”

Implementarea mergea destul de anevoios. Lucram practic singur, iar exemplele de cod pentru alte procesoare folosite prin firmă nu mă ajutau deloc, fiindcă nu pricepeam o iotă. Nu sînt programator, iar cunoştinţele mele de C sînt cel mult mediocre. Drept urmare, purced în stilul meu, trăgînd apa peste toate modificările inutile făcute în cod şi luînd-o de la zero absolut. Cu un strop de noroc, pe la jumătatea lunii reuşesc să-i dau de cap şi să fac o comunicaţie bidirecţională pe UART, sperînd să funcţioneze şi pe ECU, care folosea ISO-K.

Odată rezolvată comunicarea, am implementat rutinele critice cît am putut de repede, neavînd însă posibilitatea de a le şi testa. Din cauza timpilor de execuţie variabili, comunicaţia mea pîrlită, „rezolvată” prin polling, funcţiona cum şi cînd avea chef. Scopul meu era s-o fac nuke-proof, ori pentru asta aveam nevoie de întreruperi. Din momentul ăsta am început să mă transform.

Foaia de catalog conţinea informaţii legate strict de perifericele ataşate celor cinci procesoare, însă niciun cuvînt despre ele. Am început să caut pe net detalii legate de ele, debusolat complet de numele regiştrilor pe care-i tot găseam prin cod şi diverse forumuri, însă nicăieri în foaia mea de catalog. Am căutat atît de des încît Google mi-a cerut de vreo două sau trei ori cod de confirmare că nu sînt search-bot. Nici nu ştiam că au implementat aşa ceva…

Spre sfîrşitul lunii îmi dau seama că regiştrii pe care-i căutam nu erau în foaia de catalog dintr-un motiv foarte simplu: Cortex-M4 e acelaşi nucleu indiferent de producător, doar perifericele sînt diferite. Drept urmare, m-am dus direct la pagina celor de la ARM, găsind tot ce mă interesa. În orice caz, volumul de informaţii necesare mă copleşise. Obişnuit cu compilatorul din CodeVisionAVR, care mă scutea de muncă, am fost şocat să văd că treburile nu stau la fel în cazul Cortexului. Aici trebuia să modific în makefile, linker şi rutina de start, care era scrisă în asamblare. Am modificat pînă le-a luat dracu’, de nu mai mergea nici compilare, nici nimic. Enervează-te iar, shift+delete, copiază ultimul snapshot funcţional, începe cu întreruperile de la zero, urmărind exemple de cod şi documentaţia de la ARM.


Decembrie 2015 – „Fără speranţă.”

Dau din întîmplare de o notă de aplicaţii de la Atmel (că şi ăştia folosesc Cortex), în care era explicat frumos ce e cu întreruperile. Cînd m-am prins cum funcţionează blocul de NVIC aveam codul scris ca la carte, însă nu funcţiona deloc şi epuizasem toate posibilităţile care-mi trecuseră prin cap. Rămas fără idei, îmi întreb alţi colegi, încercînd să fac paralele, scriu pe forum, le trimit nesimţiţilor de la Freescale un mail în care ataşez codul complet şi îmi storc ultima brumă de inteligenţă în căutarea problemei. Culmea, îmi răspunde un tip de la Freescale, spunîndu-mi că am codul în regulă, dar că nu vede nicăieri către ce nucleu rutez întreruperile. Atunci mi-a picat fisa! Setează registrul de interrupt routing, compilează, încarcă aplicaţia pe card, boot şi VOILA! Nu merge perfect, dar imediat cum am executat rutina de software interrupt, debuggerul a oprit execuţia codului pe motiv de vector catch. Eram pe calea cea bună, însă orice întrerupere îmi făcea codul să sară la începutul programului, ca şi cum şi-ar fi dat reset. Ucisesem watchdogul ca să fiu sigur că nu e de la pramatia aia, absolut toţi vectorii erau trataţi, dar fără succes.

Am început iar să mă transform, căutînd furios motivul. Eram un pachet de nervi şi atît de obosit mental încît nu mai eram capabil să fac faţă unei conversaţii banale. În capul meu dansau biţii din regiştri şi se derulau pas cu pas linii de cod. Începusem să modific aiurea, doar-doar văd vreo chestie care-mi scăpa, reuşind la un moment dat contraperformanţa ca pentru două versiuni ale aplicaţiei să am trei comportamente diferite. Puţin a lipsit atunci să nu fac placa bucăţi. Rămăsesem din nou fără explicaţii, eram în culmea oboselii mentale, iar pe 12 decembrie mi-am luat un shutdown ca la carte.

Somnul adînc avut la PPPL se pare că a făcut minuni cu mine. Încă nu-mi dispăruseră liniile de cod din cap, însă se putea discuta cu mine şi cred că nici nu mai înjuram aşa mult. Problemele nu dispăruseră ca prin minune, dar la un moment dat văd cardul pe masă, debuggerul conectat şi codul rulînd! Abia atunci mi-am dat seama cum funcţiona mîrşăvia. Utilizînd Atmeluri de vreo opt ani, am presupus că toate funcţiile sînt rulate din flash, SRAM-ul fiind exclusiv pentru date şi stivă. Ei bine, M4-le meu nu funcţiona aşa, ci avea un bootloader care copia tot codul din flash în SRAM şi-l executa de acolo. Dacă n-aş fi văzut codul mergînd cu SD-ul scos, probabil că mai dura ceva timp pînă să-mi dau seama. Era absolut clar că legăturile dintre vectorii de întrerupere şi rutinele de tratare erau inexistene. Fără prea multă speranţă, am copiat adresele vectorilor în SRAM şi am executat codul. FUNCŢIONA! Nu-mi venea să cred şi ţopăiam ca o capră beată, cu rînjetul pînă după urechi, uşurat de toate gîndurile care se învălmăşiseră în cap pînă atunci. Cu o zi înainte de concediu am dat release la noua versiune bazată pe întreruperi, extrem de încîntat de reuşită. Aveam două luni avans.


Ianuarie 2016 – „Da’ mai aveţi multe?”

Ianuarie a început în forţă, cu şase proiecte la care lucram simultan:

  1. Aveam ECU real şi trebuia să continui dezvoltarea pe el;
  2. Implementat cod pentru un Atmel ca să se poată face update prin intermediul lui la un Spartan6;
  3. Ucis gîndacii din altă placă;
  4. Verificat schema şi cablajul unui bou care n-are vreo legătură cu proiectarea;
  5. Două proiecte pe acasă (pe unul l-am anulat, pe celălalt l-am amînat).

Spre sfîrşitul lunii eram din nou legumă, cu o medie de vreo cinci ore dormite pe noapte, din nou incapabil să mă concentrez asupra altceva. Parcă trecuse un an de la începutul anului..


Februarie 2016 – „Nu am aia, aia şi ailaltă.”

În a doua săptămînă terminasem tot ce aveam de făcut, mai puţin ECU-ul real la care nu mă puteam conecta cu debuggerul sub nicio formă. Nu puteam da flash, nu puteam încărca în SRAM, nu funcţiona nimic. Nu mă stresam foarte tare, deşi aveam un moment de deja-vu cumplit. Cu chiu, cu vai, reuşesc să descopăr că un alt controller de pe placă ţinea resetul ăstuia blocat din cauza unui firmware incorect. Nu aveam programator, nu aveam firmware, nu aveam nimic, totul trebuia comandat şi lua o groază de timp. Modul meu de lucru e destul de alert şi nu-mi place să stau pe tuşă prea mult. Dacă am văzut aşa, m-am apucat de studiat schema, am cerut cablajul de la un neamţ şi am hăcuit placa. Mă puteam conecta la ea cu debuggerul, însă fără să pot încărca ceva în flash.

Fiindcă problema flashului era cunoscută, m-am apucat de verificat şi adaptat codul, realizînd rapid că n-am cum comunica cu ECU-ul din cauza nivelelor diferite de tensiune. Îmi fac o interfaţă USB ⇔ ISO-K din resturile de la alte proiecte mai vechi, conectez şi… Surprizele au fost destul de mari. Comunicaţia pe ISO-K foloseşte o singură linie atît pentru transmisie cît şi pentru recepţie, iar acum trebuia să scap de ecoul rezultat. Mi-a mîncat nervii aproape toată luna, fiindcă unul din regiştri nu se comporta în modul descris în foaia de catalog, iar întreruperea de recepţie se activa şi la transmisie. Pînă la urmă i-am venit de hac.


Martie 2016 – „Sfîrşitul nu-i aici.”

Rezolvasem toate problemele legate de hardware, puteam inclusiv să urc binarele în flash, terminasem de implementat toate cerinţele critice şi încă unele în plus faţă de ce mi se ceruse, iar acum scriam liniştit la documentaţie. Eh, cum eram eu aşa, relaxat, într-una din şedinţe aflu că nu e nimeni disponibil să preia proiectul mai departe şi că voi rămîne să lucrez de capul meu pînă în iulie. Detest protocoalele de comunicaţie dintotdeauna, dar numai de implementat din astea am avut parte. UART, SPI, I2C, 1 wire, SENT, M-module mezzanine şi alte tîmpenii custom care mi-au mîncat viaţa şi nervii. Oricum, de ce ţi-e frică nu scapi. Relaxarea mi-a fost spulberată cînd am auzit că am de scris drivere pentru CAN şi Ethernet. Mi s-a zbîrlit părul în cap ştiind că urmează o altă perioadă nasoală.


Aprilie 2016 – „Să mori tu că merge!!”

Ştiam de CAN că e împuţit, aşa că mă pregătisem pentru ce era mai rău. Înainte să încep să scriu cod am adunat o grămadă de note de aplicaţii, triindu-le în funcţie de modul în care au fost scrise pînă am rămas la cîteva pe gustul meu. Nenorocitul meu de controller avrea vreo 40 de regiştri pe 32 de biţi, adică o droaie de moduri în care puteam eşua şi sorţi foarte mici de izbîndă. M-am înfipt în ei cu tupeu şi sfîrşind în frustrare. Ca de obicei, primul lucru pe care-l fac la o comunicaţie e s-o forţez să trimită ceva. Pusesem un frame oarecare şi-l pîndeam cu osciloscopul la ieşire. Nimic. Mort. Zero. Mă aşteptam să văd semnalul de idle de 2,5V pe liniile mele de CAN, dar nu mişca abslut nimic pe linia respectivă. Iau iar schema la purecat, remarcînd prezenţa unui TJA1145. Superb! Transceiver deştept, comandat prin SPI, nu ştiu pe nimeni în firmă care să-l mai fi folosit. Tuzmorţiimătiidejeg, deci tre’ să scriu drivere şi pentru ăsta. Caut foaie de catalog, găsesc, mă iau cu mîinile de cap. Altă serie de regiştri…

Termin cu SPI-ul în vreo patru zile, însă pe liniile de CAN tot nu văd nimic, în afara unui semnal corect de idle, menţinut pentru vreo secundă. Lipesc sîrmăraie pe ECU, pun sondele de la osciloscop şi încep să scriu şi să verific dacă merge sau nu, ce se întîmplă dacă…, cum fac ăla să… Mă rog, bîjbîielile clasice. La un moment dat foloseam 8 canale: 4 pentru SPI (care mergea corect, recepţionam tot ce trebuia), 4 pentru CAN (RX, TX, CAN_L, CAN_H). Pînă la urmă obţin un errror frame care se vedea clar pe CAN TX, dar în afară nu ieşea absolut nimic. Făcusem rost de un laptop cu care puteam trimite frame-uri de CAN, dar nici astea nu treceau de transceiver, care era configurat cît se putea de corect. Începuse să mă dispere. Iau din nou schema la purecat, pun un semnal de wake-up extern care face exact ce face şi rutina mea de SPI, îmi dau seama că ceva nu e bine, dar nu înţeleg ce.

Mă uit mai atent pe schemă şi văd că pişatul de transceiver foloseşte două tensiuni de alimentare: una de 3,3V pentru partea de SPI şi una de 5V pentru partea analogică. Evident, ultima lipsea cu desăvîrşire, constatînd că e comandată de alte două controlere (ăla hăcuit şi încă unul). Ce să te mai complici cu firmware, sîrmă peste MOSFET şi drum bun. Îi dau gaz şi SURPRIZĂ! CAN-ul meu mergea în draci. Cu baud rate greşit, fără acknowledge, că n-avea cine să-i dea, dar mergea! Mi-a mai luat o săptămînă să termin driverele şi am dat alt release, mîndru de mine.

Am fost mîndru două zile, fiindcă miercurea trecută m-am trezit cu un neamţ că-mi zice că lui nu-i merge şi că i-a murit inclusiv comunicaţia pe ISO-K. Vaai! Aceste cuvinte ne doare! Pun codu’ la mine, îi dau în guşă, merge fără probleme. Tipul îmi zice că-l rulează din flash, fără debugger. Eu îl rulam din SRAM, cu debugger. Dau şi eu flash, bineînţeles că nu mai merge nimic. Tuzmorţiimătiidejeg, ce te-a apucat? Şi sapă, retardatule, să vezi ce are, şi încearcă să afli unde se blochează, fiindcă jigodia mergea ireproşabil de cum conectam debuggerul, fără să mai încarc nimic în SRAM.

După cîteva zeci de compilări am reuşit să izolez problema, codul comportîndu-se ca şi cum ar fi dat de un fel de race condition. Da, mă, bine că eşti tu deştept. Vineri dimineaţa schimb două linii de cod între ele şi mă aleg cu hard fault. Nu mai mergea nici în SRAM, nici în flash. SUPERB! Fiindcă acum aveam exact acelaşi comportament în ambele situaţii, m-a ros curiozitatea să văd de ce se întîmplă aşa. Două ore mai tîrziu descoperam motivul: debuggerul curăţa toată memoria, în timp ce codul meu curăţa doar cît avea nevoie. Printre ce avea el nevoie mai era o zonă nemenţionată în foaia de catalog, iar după ce-am dat cu mopul şi pe acolo… ei bine, zbîrnîie. Am încercat să-i trimit tot felul de erori şi jeguri, să scurtcircuitez liniile în timp ce merge, doar-doar îl fac să crape. N-am reuşit, deci pot considera că e suficient de bomb-proof.

Am scăpat de CAN, dar m-am apucat de Ethernet. Nu ştiu nimic despre cum funcţionează, n-am idee ce-mi trebuie, nu am detalii legate de un cip, dar… cînd m-a împiedicat pe mine asta să nu mă apuc de lcuru?

Anunțuri
Etichetat cu: , ,
Postat in Electronică, Ţuicisme

Ai ceva de zis? Bagă mare:

Te rog autentifică-te folosind una dintre aceste metode pentru a publica un comentariu:

Logo WordPress.com

Comentezi folosind contul tău WordPress.com. Dezautentificare / Schimbă )

Poză Twitter

Comentezi folosind contul tău Twitter. Dezautentificare / Schimbă )

Fotografie Facebook

Comentezi folosind contul tău Facebook. Dezautentificare / Schimbă )

Fotografie Google+

Comentezi folosind contul tău Google+. Dezautentificare / Schimbă )

Conectare la %s