AVR core - folytatas

Egy kicsit zavart hogy a soft AVR core-s projektben ez az egyebkent elegge AVR-specifikus, 6 bit mely, 8 bit szeles I/O buszt a CPU aszinkron modon olvassa es szinkron modon irja. Azaz mig a program-memoria illetve az adatmemoria rendes szinkron ROM/RAM-kent viselkedik (igy mind az olvasasi, mind az irasi ciklus eredmenye a kovetkezo orajelre jelenik meg a CPU es/vagy a memoriak szamara), az I/O port eseten az olvasas a jelenlegi implementacioban aszinkron. Gondoltam kicsit utanajarok ennek a dolognak mit lehet itt tenni hogy egy elegansabb, jobban fenntarthato, jobban ujrafelhasznalhato cuccmany legyen ebbol az egeszbol. 

Jelenleg az I/O port eseteben az aszinkron olvasas miatt egy kombinacios logikai halozat eredmenyet latja a CPU ugyanabban az orajel-ciklusban mint amikor ott omaga eloallitja a cimet. Ez egy ketlepeses instruction pipeline esen - ahol a  ebben a masodik stage-ben tortenik az execution es a writeback is - mar okozhat merheto lassulast, meghat nem is elegans. Mindezek mellett persze az egesznek legnagyobb hatranya az lett a gyakorlatban hogy egy I/O portra megirt periferia (UART, timer, CRC accelerator, barmi) nem csereszabatos, azaz az adatmemoriat kihajto buszra nem lehetett railleszteni valtoztatas nelkul amennyiben ugy hozta (volna) a szukseg. 

A tanulsag kis HDL-ezes es programozgatas utan az lett hogy ezen harom tulajdonsag, miszerint:

  • kiszamithatoan es mindig pontosan egy oraciklus alatt vegrehajthato AVR I/O muveletek;
  • teljesen szinkron I/O busz; es 
  • single-port memorianak megfeleltetheto I/O busz 

kozul pontosan maximum kettot valosithatunk meg de a harom dolog egyszerre nem fog menni. A jelenleg elerheto/letoltheto AVR SoC verzio az a "teljesen szinkron I/O busz" megkotest dobta el, a mostani fejlesztoi valtozatban/forkban pedig a "single-port memorianak megfelelo I/O busz" kitetelt dobtam el. Igy az I/O portra nemcsak egy 6 bit szeles cim, hanem ket 6 bit szeles cim is kimegy, kulon cimvezeteke van az olvasasi illetve az irasi ciklusnak. Amennyiben ezt a dual port periferiat szeretnenk az adatbuszra kotni (ami single port) akkor persze a ket cim kozositheto hiszen az adatbuszon egy orajel alatt vagy csak irasi vagy csak olvasasi muvelet tud zajlani. Igy a periferia-modulok is ujrafelhasznalhatoak meg erezhetoen gyorsabb is lett a rendszer (a kiserleti Lattice FPGA-n kb 15%-os gyorsulassal).

A dolog hattere az az hogy egy 

out 0x2A, rxx
in  ryy, 0x2A

vagy egy

out 0x2A, rxx
in  ryy, 0x2B

utasitas-sorozat eseten, teljesen szinkron I/O buszt feltetelezve az elso OUT utasitas irasi ciklusa illetve a masodik IN utasitas olvasasi ciklusa egyidoben tortenik. Persze ilyesmi aprosagokra is kell figyelni a periferia "gyartojanak" hogy a szinkron iras + szinkron olvasas az write-through legyen (mint pl a fenti elso pelda eseteben is ha a port address az OUT-nal es az OUT-ot azonnal koveto IN-nel ugyanaz), de erre halistennek a gyakorlatban nagyon keves esetben kell tenyleg figyelnunk. A masodik peldan meg latszik hogy miert kell dual port busz a periferiak fele (hiszen egyidoben a CPU mind a 0x2A, mind a 0x2B cimmel is operal).

Vegkonkluzio: egyelore nincs. Vagy hasznaljunk RISC-V-t. Ja hogy ott meg prediktalhato oraciklus-szamok nem lesznek. 

Hozzászólások

Ha a 2. és 3. tulajdonsághoz ragaszkodsz, akkor azt (halványan) érteni vélem, hogy 1 órajel-ciklusban egy "in"-nek nem lesz meg az eredménye, tehát az utasításvégrehajtás blokkolódni fog, és várni fogsz a bejövő adatra -- de miért nem determinisztikus, hogy az adat mennyi órajel-ciklus alatt érkezik meg? Valamint felső becslés nem elegendő az "in" futásidejére?

További (zöldfülű) kérdésem: ha az 1. és 3. tulajdonságot választod (= "jelenleg elérhető AVR SoC"), akkor konkrétan mi az az adat, amit az "in" 1 órajel-cikluson belül visszaad ("kombinációs logikai hálózat eredménye")?

Igenigen, a determinisztikussag az megoldhato, pl hogy az IN/OUT legyen ugyanaz mint az LD/ST (azaz 2-2 oraciklus). A feltetel/hangsuly inkabb azon van hogy ezeknek egy oraciklus alatt kell lemennie es nem ketto. Tartani akarom magam a klasszik AVR-hez ilyenertelemben (is), szoval az elso tulajdonsag az ezert lenne fontos.

A masodik tulajdonsag az most annyit takar hogy jelenleg (az opencore-os verzioban) kombinacios logikai halozat valasztja ki a kimeneti regiszter erteket a periferiaban:

/* I/O read: */
reg [7:0] io_do_data;
always @(*) begin
        casex (io_a)
                2'b00: io_do_data = TCNT[7:0];
                2'b01: io_do_data = TTMP[7:0];
                2'b10: io_do_data = TCR;
                2'b11: io_do_data = TSR;
        endcase
end
assign io_do = io_re ? io_do_data : 8'b00000000;

Ugye ez nem elegans, mert ez nem egy szinkron memoria. Raadasul az olvasas (io_re) megvaltoztathatja az allapotat is a rendszernek. Egy UART-nal egyertelmu hogy miert (kiszedi az adatot a receive FIFO-bol), de meg egy ilyen timer-nel is ahol 16 bites regisztereket kell atomi modon olvasni:

always @(posedge clk) begin

        if (io_we & ~io_re) begin
                if ( io_a==2'b01 ) TTMP <= io_di;
                if ( io_a==2'b10 ) TCR  <= io_di;
        end else if ( io_re ) begin
                if ( io_a==2'b00 )
                        TTMP <= TCNT[15:8];
        end

end

Es hogy egy if ( io_re ) feltetel van itt is meg ott is... hát... :)

Nem vagyok biztos benne, de rémlik, hogy az AVR az órajel mindkét élére csinál valamit. Ezért tud 1 órajel alatt megcsinálni dolgokat, ami igazából két lépés. Nem ez hiányzik nálad? Csak így messziről beleugatva.

elso OUT utasitas irasi ciklusa illetve a masodik IN utasitas olvasasi ciklusa egyidoben tortenik.

Ez a valódi AVR esetén így van? Mi ennek az értelme? Ha szinkron memóriát akarsz használni ilyen 1 ciklusos írás/olvasásra, adj a BRAM-nak negált órajelet.