FPGA:0002 – Cypukas

Kai jau kažkas suveikė ir signalas perėjo iš vieno mikroschemos šono į kitą, reikia tą signalą kaip nors modifikuoti.
Aišku galima iš bibliotekos prisidėlioti visokių loginių elementų į schemos puslapį ir viskas veiks. Bet mūsų užduotis pasigaminti modulį kuris veiks pagal mūsų norus. Ką nors nestandartinio.
Tačiau pradžiai reikia pasidaryti pirmą periferinį įrenginį- mažyčiuką garsiakalbiuką. Aišku nesamonė jungti tiesiogiai garsiakalbiuką prie LVTTL kojos, reikėtų kokio nors buferio, tačiau bus gerai ir tiesiogiai. Aš panaudojau cypuką iš kompų motininės plokštės. Kad netrumpinti FPGA kojos, nuosekliai įlitajau nedidelį rezistorių- kad srovė neviršytu leistinos mikroschemos išėjime.

Mūsų FPGA turi vienintelį CLK šaltinį 50MHz osciliatorių (25MHz gal pas ką nors). Tokio dažnio tikrai niekas negirdi, nebent radio imtuvai. Todėl dažnį reikia sumažinti. Lengviausia tai padaryti su skaitliuku.

Todėl atsidarom nauja failą, pasirenkam kaip “verilog” ir rašom tokį softą:

module beep(input clk, output speaker)
reg[16:0] counter;

always @(posedge clk)
begin
    counter < = counter+1;
end
assign speaker = counter[16];
endmodule

module – tai komanda kuri atskiria modulius. Jų gali būti daug viename faile. Toliau eina beep- taip modulio pavadinimas, skliaustuose modulio įėjimai ir išėjimai. Galima atskirai surašyti tik laidus, o juos aprašyti kitoje eilutėje, o galima iškarto įvesti aprašymą. Input- įėjimas, Output- išėjimas.
Toliau aprašomi lokaliniai kintamieji… gal geriau taip nevadinti, nes tai realus laidai, o šiuo atveju 17 bitų registras. Registras pasižymi tuo, kad jis “įsimena” savo reikšmę ir ją laiko. Visi registrai yra sinchronizuojami su clk signalu. Komanda always @ (posedge clk) reiškia: visada kai (clk turi perėjima iš žemo lygio į aukštą) darom kažką.
begin-end grupė atskiria grupę komandų, čia panašiai kaip {} skliausteliai C kalboje. Jei komanda viena, galima ir vienoje eilutėje užrašyti, be tų begin-end.
Komanda assign jau ne sinchronizuota su clk signalu. Ji paprasčiausiai priskiria 16 registro bitą prie laido speaker.
Dabar svarbus sunkiai suvokiamas teiginys. Kaip programuojam procesoriuką, viskas vyksta linijiškai, t.y. žingsnis po žingsnio: komanda, komanda, ciklas, pertraukimas, ciklas, komanda… ir t.t. Čia čia, FPGA/CPLD viduriuose visi procesai (jei nenurodyta kitaip) vyksta tuo pat metu. Galima sakyti lygiagrečiai. Jei šioje programoje kažkas skaičiuojasi pagal clk signalą, tai lygiai tuo pat metu persirašo to speaker signalo vertė. Todėl rašant programas negalima taip sumakaluoti source kad tas pats signalas tuo pat metu įgautu skirtingas vertes. Pas MCU paprasčiausiai persirašo PASKUTINĖ reikšmė. Tuo tarpu čia dvi skirtingos reikšmės patenka į vieną laidą, o tai reiškia… trumpą jungimą. Quartus softas paprastai randa tokias klaidas, tačiau labai dažnai pradžioje jos atrodo tokios nelogiškos.
Ką šita programa daro? Ogi skaičiuoja clk signalą ir pasiima 17 bito reikšmę. T.y. daliną dažnį. Jei naudosit 25MHz clk signalą, naudokit 16 bitą (counter[15]).
Užseivinam šitą failą kaip beep.v. Kairėje kortelėje “files” atsiranda mūsų failas. Pažymim jį ir spaudžiam dešinį pelės klavišą, “Create symbol files for current file”. Gaunam klaidas! Čia aš specialiai padariau, kad matytusi kaip programa reaguoja į klaidas. Ji rodo net trys klaidas! O klaida paprasčiausia- pirmoje eilutėje, nėra kabliataškio eilutės gale- tik viena klaida. Čia paprasčiausiai viena klaida sukelia kitą klaidą ir taip lavinos principu.
Įdėjus kabliataškį viskas susitvarko – nei klaidų nei įspėjimų. Einam atgal į schemą. Tą pačią kaip ir pirmoje pamokoje ar kuriam iš naujo. Reikia dviejų mikroschemos kojų: clock ir išėjimo. Tik pašalinam laidą jungiantį tuos abu pinus. Spaudžiam “Symbol Tool” piktogramą (panaši į loginį elementą) ir iššoka lentelė. Kairėje matosi mūsų projekto folderiukas ir dar sisteminė biblioteka. Joje yra daug standartinių loginių elementų bet jie dabar nereikalingi. Dar yra toks super mygtukas pavadintas “MegaWizard”- čia galima prisigeneruoti specifinių elementų. Nuo paprastų skaitliukų kur ką tik patys parašėm iki Nios kompiuteriuko. Dabar pasirenkam mūsų “project” folderiuką ir ten randame naują simbolį pavadinimu “beep”. Pasirenkam jį ir dedam į schemą ir sujungiam laidus:
schema
Užsaugom projektą ir sukompiliuojam. Supučiam į FPGA ir prijungiam garsakalbiuką. Pasigirsta nemalonus cypimas- 50 000 000 / 131 072 = 381.4697… Hz. Kodėl 131072? Ogi tai 2 pakelta 17 laipsniu. Jei naudojot kitokį pradinį dažnį, aišku gausis visiškai kitoks.
Tačiau melomanams toks dažnis raižo ausis- tonas negrynas. Sakysim norime LA tono kuris yra lygiai 440Hz. Paprasta- reikia pradinius 50MHz padalinti iš reikiamo tono ir gausime daliklį: 113636,3636… wow nešvarus skaičius ir dar priedo periodinė trupmena. Suapvalinam iki sveikų dalių- niekas iš klausos tos paklaidos neatskirs.

Kuriam naują modulį kurį jau pavadinam music:

module music(input clk, output speaker);
reg [16:0] counter;
always @(posedge clk)
begin
 if(counter == 113636) counter <=0; else counter <= counter+1;
end
assign speaker = counter[16];
endmodule

Softas paprastas ir panašus į ankstesnįjį. Tik dabar rezetuojam skaitliuką kaip pasiekiam norimą skaičių.
Tačiau dar biški negerai! Skaitliukas kiek asimetrinis (duty cycle nėra 50%) nes skaitliukas rezetuojamas kažkur, o ne pats persiverčia. Todėl daroma taip- pradinis dažnis daromas du kartus didesnis, 880Hz. O vėliau gražiai padalinamas perpus.

module music(input clk, output reg speaker);
reg [15:0] counter;
always @(posedge clk) if(counter==56818) counter <= 0; else counter <= counter+1;
always @(posedge clk) if(counter==56818) speaker <= ~speaker;
endmodule

Čia demonstruojamas būtent tas procesų lygiagretumas- abu priskirimai atliekami vienu metu (tiesa pasakius juos galima parašyti viename always@ bloke). Kitas skirtumas- pats “laidas” speaker pavirto registru, nes jis atsimena reikšmę visą laiką, ir tik kas kažkielintą ciklą invertuoja savo reikšmę. Komanda tildė “~” reiškia invertuoti signalą.

Dabar pamieravau su oscilografu signalą ir matau, kad kažkodėl nėra grynu 440Hz, o 438Hz. Gal suklydau skaičiuodamas, o gal mano 50MHz nėra labai 50. Reikia keisti daliklio koeficientą. Tačiau kiekvieną kartą ieškoti kažkur užkoduoto skaičio greitai atsibos. Todėl tokius skaičius galima įrašyti kaip parametrą. Tada galima skaičius keisti operatyviau ir net iš schemos failo. Darom taip:

module music(input clk, output reg speaker);
parameter daliklis=56818;

reg [15:0] counter;
always @(posedge clk) if(counter == daliklis) counter <= 0; else counter <= counter+1;
always @(posedge clk) if(counter == daliklis) speaker <= ~speaker;
endmodule

Dabar jau reikia per naujo sukurti simbolio failą. Taip pat schemoje panaudoti “refresh symbol” jei pats softas nepasiūlė.
schema su parametru
Dabar daliklį galime patiuninti nebeieškodami reikalingo modulio. Pvz. anot mano oscilografo 440Hz gaunasi kažkur kai parametras būna 56300 ribose. 🙂
Beja, atkreipkit dėmesį kaip kinta counter registro plotis- tai 15 tai 17 bitų. Kodėl? Kad tilptu visas skaičius ir kad be reikalo nesinaudotu bitukai. Tiesa, Quartus softas, jei pasinagrinėti tuos wariningus, pats dažnai optimizuoja registrų pločius. Ir beja parodo schemos dalis kurios pavyzdžiui nieko nedaro. Verta pasiskaitinėti.

ŠIOJE PAMOKOJE išmokom rašyti verilog modulius ir įterpti juos į schemą. Susipažinom biški su verilog kalba ir jos kintamaisiais…tfu.. laidais.
Straipsnyje naudota medžiaga iš fpga4fun puslapių. Bet biški pakeista.
Dėmesio! Kartais WordPress variklis užsispiria ir iškraipo tekstą.

Leave a Reply

Your email address will not be published. Required fields are marked *