FPGA:0004 – SVGA

Pažaidus su LEDais, pabandykime prijungti kokį nors VGA monitorių. Kadangi mano eksperimentinis monitorius yra LCD su 1024×768 raiška, tai man reikia susigeneruoti tokią “video plokštę” kuri palaikytu tokią rezoliuciją. kodėl tiksliai pataikyti į “native resolution”? Ogi kad pažaisti su smulkiais objektais ir taimingais.
Iš senesnio mano puslapio galima sužinoti standartizuotus VESA taimingus ir video generavimo koncepcija. Internete pilna visokių video generavimo schemų ir variantų, tačiau aš naudosiu savo. Bent jau aiškinimuose kaip veikia. Pirmiausia reikia susikonstruoti VGA kištuko “šieldą” arba “add-oną”. Kadangi ateityje norėsiu pabandyti daugiau spalvų, tai nutariaus sulituoti 4 bitų DACą kiekvienai spalvai. Tai duos iš viso 4096 spalvas. Tiek pat, kiek sugebėjo rodyti pirmieji Amiga kompiuterio modeliai.
Kiekvienas save gerbiantis VGA monitorius turi 75 omų apkrova savo viduje, o maksimalaus raiškio įtampa kažkur apie 0.7V (700mV). Mūsų “video plokštė” turi tik 3.3V LVTTL išėjimus, ir rodos max 20mA per koją (su apribojimu visam blokui ir čipui). Todėl reikia pasidaryti patį primytyviausią DACą (digital analog converter). Tam reikes apie 0.5k, 1k, 2k ir 4k rezistorių. Kadangi tokie tiksliai rezistoriai neegzistuoja (ypač mano stalčiuje), tai pasirinkau: 512R, 1K, 2K, 3.4K. Kiek paskaičiavus ir gaunasi, kad įtampa spalvos laide svyruos nuo 0 iki maždaug 700mV. O kaitaliojant bitus įtampa keisis maždaug tiesiškai. Beja tą ir parodė vėliau osciloskopas.
VGA DAC 4096 colors.

Sulituojam po 4 rezistorius prie kištuko, kitus reziku galus sujungiam į vieną laidą ir jungiam prie VGA kištuko (1,2,3 = R,G,B). Dar reikalingi sinchronizacijos impulsai HS ir VS. Tiesa pasakius aš painioju kuris yra kur… Juos jungiam prie monitoriaus per maždaug 200 omų rezistorius. Kam tie rezistoriai? Ogi apsaugoti FPGA jei netyčia kur užtrumpinsim ar papuls į 3 ar 5V iš monitoriaus. (5v duos apie 25mA srovę per FPGA. Turi laikyti ir nesvilti).
Rezistorius geriau sulituoti tvarkingai nuo didžiausio į mažiausia. Tačiau aš sulitavau bet kaip, nes tingėjau žiūrėti tuos dryželius. Vistiek FPGA pin planeryje galima viską greitai perkomutuoti. Nu dar reikia žemės laidelio ir jau galima jungti… bet nėra signalų.

Dabar prasideda FPGA programavimas. Pradedam nuo pixel_clock signalo. Pixel clock tai dažnis kuris atitinka vieną pixelį ant ekrano. Dėl technoliginių subtilybių geriau turėti pagrindinį sistemos clk dvigubai didesnį nei pixel_clock. Paprasčiausiai taip lengviau poto žaisti su visokiom atmintim ir t.t.
Internete ar mano puslapiuose apie VGA generavimą pažiūrim į skaičius: 65MHz pixel_clock, arba 130MHz “sisteminis” clock. Vaje, mūsų plokštė turi tik 50MHz signalą. Va čia ir atsiranda naujas dalykėlis kurį turi ciklonas, bet neturi maxas- PLL. PLL tai integruota į čipą phase locked loop dalis kuri leidžia generuoti kitokius dažnius. PLLas leidžia pradinį clk signalą padauginti ir padalinti tam tikrose ribose. Jei 50MHz padauginti iš 13 ir padalinti iš 5 gausim? 50*13=650, 650/5=130… Kiek ir reikėjo. 🙂
Darom taip: spaudžiam detalytės ikoną (Symbol Tool), spaudžiam “MegaWizardo” mygtuką. Create a new custom megafunction variation. NEXT. Iš sąrašo kairėje pasirenkam: I/O, ALTPLL. (I/O grupėje, nes pllai dažnai naudojami ryšyje su išore). Parašom PLLo failo vardą, kad ir PLL01. NEXT. Užsidega naujas didelis langas, kurioje kairėje formuosi PLL blokas, o dešinėje parametrai. Ten automatiškai pasirenka mikroschemos greičio gruopė (pas mane 8 ), o kitoje eilutėje klausia koks dažnis bus įėjime. Ten stovi 100MHz, pataisom į mūsų 50. Kiti mygukai defaultininiai (internal feedback, normal mode). NEXT. Dabar siūlo sukurti PLLo valdymo ir informacinius išvadus. Vienas leidžia per naujo paleisti pastrigusi PLLą, kitas rodo, kad PLLas stabilizavosi ir veikia. Kol kas mums šito nereikia. Nuimam varneles. Mūsų “blokas” lieka tik su dviem kojom. NEXT. Toliau klausia dėl alternatyvinio input įėjimo. Mums jo nereikia. NEXT. Dabar “c0 – Core/External Output Clock” dalyje reikia suvesti norimas reikšmes. Galima tiesiog pažymėti “enter frequency” ir softas pabandys paskaičiuoti galimus daliklius ar artimiausią galimą. Arba įvesti rankutėmis daliklius-daugiklius. Kaip be įvesi, bet 13/5 susiveda. Ir viršuje dega mėlynai, kad viskas gerai. NEXT. Dabar siūlo dar vieną išėjimą paskaičiuoti. Jo nereikia, todėl NEXT. Ir dar NEXT. Dabar parodo simuliavimo bibliotekas kurias kurs. Nesvarbu. NEXT. Dabar rodo kokius failus sugeneruos. FINISH.
Dabar turim naują dvikojį elementą kurį įmetam į schemą ir prijungiam prie 50MHz kojos. Tiesa užmirsau, kad PLLas leidžia generuoti skirtingo duty-cycle ar fazės signalus. Kol kas mums tai nesvarbu.
Dabar schemoje turim 130MHz signalą kurį naudosim savo “sistemos” taktavimui. Kuriam naują verilog failą ir jame modulį vesa1024x768. Modulyje kaip minimum turi būti įėjimas clockui, išėjimas sinchronizacijai ir dar keli, kuriuos aprašysime eigoje.
Pirmiausia pasimažinam clocką iki pixel_clocko. Tai paprasta ir jau mokame:

always@(posedge clk) pixel_clock <= ~pixel_clock;

Toliau mano teorinis generatorius remiasi x ir y skaitliukais. Čia darome kaip ir kokioje C kalboje: didinam x, kai x pasiekia kažkokią reikšmę, x nulinam ir tuo pačiu didinam y. Kai y pasiekia kažkokią reikšmę, y nulinam ir taip sukam nuo pradžių, non-stop visą laiką. Tai daro šis soursas:

always@(posedge pixel_clock)
begin
    if(x<max_x)
        begin
            x <= x+1;
        end
    else
        begin
            x <= 0;
            if(y<max_y) y <= y+1; else y <= 0;
        end
end

Tie max_x ir max_y tai parametrai, į kurius įrašim skaičius iš VESA standarto.
Dabar sugeneruojam hs ir vs sinchro impulsus. Tikrai nežinau ar aš juos teisingai pavadinau, bet kadangi galimi tik du pajungimo variantai, tai poto labai lengva sukeisti laidus ir gauti video vaizdelį. Rašom taip:

always@(posedge pixel_clock)
begin
    if(x>vs_low && x<vs_hi) vs <= 1; else vs <= 0;
    if(y>hs_low && y<hs_hi) hs <= 1; else hs <= 0;
end

Čia viskas labai paprasta: kai tik x ar y papuola į tam tikrą rėžį apibrėžta parametrais, hs ir vs registrai keičia reikšmes. Dėl LCD monikų veikimo principų ir šiaip, kad netyčia nepapultu video vaizdas kai eina sinchronizavimo ir blankavimo impulsai, reikia pašalinti video vaizdą iš išėjimo, kai būna vaizdo užribis. Tam tikslui aš susigeneruoju blanker signalą. Taip:

always@(posedge pixel_clock)
begin
if(x<size_x && y<size_y) blanker <= 0; else blanker <= 1;
end

Dar iš modulio išvedam x ir y koordinates. Jas bus galima panaudoti spraitų skaičiavimui ar video ram atminties sužinojimui. Čia visas vesa1024x768 verilog modulis kad nereikėtu galvoti.
Teoriškai paleidus šį modulį prie monitoriaus, turėtu užsidegti žalias šviesos diodas, kad monitorius pajuto signalą… bet vistiek nieko nerodys. Prieš ką nors jungiant, pasidarom dar vieną “kaladėlę” mūsų projektui. Butent tą blankerį. Jis atrodo taip:

module blanker(r,g,b,blank,qr,qg,qb);
input [3:0] r;
input [3:0] g;
input [3:0] b;

input blank;

output [3:0]qr;
output [3:0]qg;
output [3:0]qb;
    assign  qr = (blank) ? 8'b00000000 : r  ;
    assign  qg = (blank) ? 8'b00000000 : g  ;
    assign  qb = (blank) ? 8'b00000000 : b  ;
endmodule

Tai paprasčiausias 3×4 bitų multiplexorius dviem kanalam- 0 ir RGB reikšmėm. Jei signalas blank=1 išėjime 0, o jei ne, tai tas kas įėjime.
Dabar viską sujungiam į tokią schemą:
VGA kontroleris FPGA
(schema pasididina, nelabai aiškiai nuvestas “blank” signalas. Jis iš vesa modulio išėjimo eina į blankerio iėjimą)

Kadangi neturim ką rodyti, kaip video informaciją panaudojam x ir y koordinačių skaitliukus.
Ir monitoriuje turim pamatyti tokį vaizdelį:
VESA VGA on FPGA

Tai pradžiai kaip ir užteks. Čia išmokom kurti PLLus ir biški paprastos logikos.

Leave a Reply

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