Autore Topic: dead  (Letto 19574 volte)

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #105 il: 27 Gennaio 2014, 21:41:03 »
Ma non potresti, ad esempio, nel primo ciclo sommare il registro e l'offset da una parte, e shiftare l'indice da un'altra parte, mentre nel secondo ciclo si sommano i due valori?

Offline Z80Fan

  • Administrator
  • Guru
  • *****
  • Post: 1671
  • Karma: +13/-2
    • Mostra profilo
    • http://z80fan.altervista.org
Re:Retargetable C Compiler
« Risposta #106 il: 27 Gennaio 2014, 22:04:22 »
Una interessante fonte di informazioni e' il progetto magic1, di sicuro l'avrete gia' visto perche' e' presente nelle cache google. E' un esempio concreto di implementazione a micro codice di una nuova architettura.

@z80fan
nota che anche il suo autore ha previsto lo state_next nel micro codice.

Si, ma come hai detto anche te, Magic-1 si basa su x86, con istruzioni a lunghezza variabile e codifica stramba, quindi serve un microcodice più potente (sopratutto per fare l'istruzione repeat).

Cmq il Magic-1 rimane il computer homebrew più completo di tutti, con anche il porting di Minix e lo stack di rete. :D

Mai visto prima, ma sembra interessante, soprattutto per le riflessioni e spiegazioni dell'autore.

Se hai un po' di tempo libero, ti consiglio di fare un giro sull' "Homebrew Computers Web Ring" (la cui homepage è qui); si trovano tanti progetti interessanti, sopratutto perchè molti sono controcorrente e con architetture strane! :D

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #107 il: 27 Gennaio 2014, 22:12:45 »
Infatti è pieno di progetti. Mi segno anche questo link, grazie.

Comunque Magic-1 m'interessa proprio per le affinità con x86. ::)

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #108 il: 27 Gennaio 2014, 22:43:24 »
Infatti è così. La stragrande maggioranza delle istruzioni è microcablata, mentre la minoranza è implementata usando il microcodice.

Io ho avuto altre idee. ::) La mia architettura è di gran lunga più semplice a livello di struttura e decodifica degli opcode, e inoltre sono veramente una piccola parte le istruzioni che richiederebbero il microcodice. :P

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #109 il: 29 Gennaio 2014, 06:51:13 »
L'implementazione, come sai, non è certo il mio forte. Ma credo che una volta risolto il problema della lunghezza dell'istruzione, il resto sia semplice, e questo vale in particolare per le CPU superscalari.
Per l'esecuzione all'interno di una singola pipeline, fra esecuzione diretta/micro-cablata e microcodice se ne esce. Considera che la stragrande maggioranza delle istruzioni eseguite è micro-cablata, e sono una piccola parte richiede l'uso del microcodice. Sì, il punto è come gestire tutto questo casino. A mio avviso quando incontri un'istruzione che richiede microcodice blocchi la pipeline ed esegui solo quello, mentre lavori normalmente nel caso normale / comune.

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #110 il: 29 Gennaio 2014, 18:42:16 »
Caspita, il PowerPC 970 possiede quattro ALU, due FPU e due unità SIMD: goloso  ;D
Sì, ma una delle unità SIMD poteva eseguire soltanto la permutazione dei dati.

Comunque quel processore aveva una pipeline troppo lunga, per cui con codice "intricato" (pieno di salti condizionali) non è che andasse bene.
Sì, il punto è come gestire tutto questo casino

quello che spaventa sono le alee sui dati e sul controllo,
Non so cosa sono le alee. :(
Citazione
se si capisce subito di che tipo di istruzione si tratta e se sono noti tutti i suoi parametri e' un conto,
Beh, ma anche se dovesse essere necessario qualche ciclo di clock in più, quale sarebbe il problema. Voglio dire: metti che ce ne vogliano 3 (sparo un numero, assolutamente a caso), su una pipeline di una dozzina di stadi (piuttosto comune di questi giorni), non mi pare la fine del mondo.
Citazione
se questi devono essere calcolati (p.e. EA complesso) iniziano a nascere problemi seri,
Bisogna vedere cosa s'intende per complesso. Per un FPGA sicuramente, perché non è in grado di gestire bene gli shift (e mi chiedo quand'è che metteranno finalmente a disposizione un po' di logica dedicata, visto che è un problema ben noto e ricorrente). Ma per un processore, prelevare un dato e shiftarlo immediatamente di 0, 1, 2, o 3 posizioni a sinistra è cosa molto semplice ed efficiente.

Prendendo la modalità più complessa, e tenendo conto del fatto che il registro indice lo possiamo ottenere facilmente già shiftato, alla fine si tratta di eseguire una somma fra 3 operandi, che mi pare sia gestibile in un ciclo di clock, giusto?

Quindi il calcolo dell'EA, alla fine, potrebbe portare via soltanto un ciclo in un processore "convenzionale".
Citazione
oltre al fatto che proprio con quei problemi di alee serve una discreta intelligenza per tenere occupati stadi replicati (p.e. 4 ALU) sopratutto con esecuzione fuori ordine.
Io penso che l'EA debba avere una mini-ALU dedicata, che esegua esclusivamente la somma di 3 operandi (di cui uno già shiftato opportunamente, quando viene prelevato).
Quanto costa alla fine un sommatore a 3 vie?
per il context del segnali di datapath ho nel simulatore tutta sta roba

Codice: [Seleziona]

    cpu_datapath.instruction = instruction;
    cpu_datapath.index       = index_i;
    cpu_datapath.class       = cpu_my_instruction_tab[index_i].class;
    cpu_datapath.size        = instruction.opcode.size;
    cpu_datapath.rs1         = instruction.opcode.rs1;
    cpu_datapath.rs2         = instruction.opcode.rs2;
    cpu_datapath.rt1         = instruction.opcode.rt1;
    cpu_datapath.is_pre_EA   = instruction.opcode.is_pre_EA;
    cpu_datapath.EA_ra       = 0xdeadff00;     /* n.a. at this step */
    cpu_datapath.EA_in       = 0xdeadff11;     /* n.a. at this step */
    cpu_datapath.EA_out      = 0xdeadff22;     /* n.a. at this step */
    cpu_datapath.source1     = 0xdeadff44;     /* n.a. at this step */
    cpu_datapath.source2     = 0xdeadff55;     /* n.a. at this step */
    cpu_datapath.source3     = instruction.const32;
    cpu_datapath.mode_rt1    = instruction.opcode.mode_rt1;
    cpu_datapath.mode_rs1    = instruction.opcode.mode_rs1;
    cpu_datapath.mode_rs2    = instruction.opcode.mode_rs2;
    cpu_datapath.alu_opp     = instruction.opcode.alu_opp;

    /*
     * default signals
     */
    cpu_datapath.enable_branch     = False;
    cpu_datapath.enable_update_rs2 = False;

    /*
     * instruction specific signals
     */
    cpu_my_instruction_tab[index_i].context();



l'obiettivo che mi ero dato inizialmente era di ridurre il + possibile le dimensioni delle instruction specific signals in modo da non dover introdurre altri stati nella fsm del sequenziatore, purtroppo non mi e' possibile ridurli ulteriormente per cui mi tocca prevedere stati specifici di ogni singola istruzione (o gestiti per classi di istruzioni)per gestirli.


p.e.
la BRA viene costruita e testa cosi'

Codice: [Seleziona]

private boolean_t test_bra()
{
    cpu_my_instruction_t instruction;
    uint32_t             s1;
    uint32_t             s2;
    uint32_t             t1;
    uint32_t             addr;
    uint32_t             offset;
    boolean_t            ans;

    /*
     * bra
     */
    s1     = 2; /* use register s1 to get source1 */
    s2     = 3; /* use register s1 to get source2 */
    t1     = 1; /* use register t1 to put result */
    addr   = 0x20;
    offset = 0x20000000;

    instruction                  = instruction_default_get();
    instruction.opcode.code      = op_bra;
    instruction.opcode.size      = opp_byte;
    instruction.opcode.rs1       = s1;
    instruction.opcode.rs2       = s2;
    instruction.opcode.rt1       = t1;
    instruction.opcode.mode_rs1  = is_reg_contents;
    instruction.opcode.mode_rs2  = is_immediate_data;
    instruction.opcode.mode_rs2  = is_reg_contents;
    instruction.opcode.mode_rt1  = is_reg_contents;
    instruction.opcode.is_pre_EA = True;
    instruction.opcode.is_pre_EA = False;
    cpu_softreg[0]               = 0; /* register r0 is always equal to zero */
    cpu_softreg[s1]              = 1;    /* rs condition */
    cpu_softreg[s2]              = addr; /* ra address */
    instruction.const32          = offset;
    cpu.reg.pc                   = 0;
    cpu_softreg[reg_PC]          = 0;
    cpu_exec(instruction);
    printf("bra(");
    printf("r%u=0x%lx", instruction.opcode.rs1, cpu_softreg[instruction.opcode.rs1]);
    printf(",");
    printf("r%u=0x%lx", instruction.opcode.rs2, cpu_softreg[instruction.opcode.rs2]);
    printf(")->");
    printf("r%u=0x%lx", instruction.opcode.rt1, cpu_softreg[instruction.opcode.rt1]);
    printf(" PC=0x%lx\n", cpu_softreg[reg_PC]);

    if (cpu_softreg[reg_PC] == (addr + offset))
    {
        ans = True;
    }
    else
    {
        ans = False;
    }

    return ans;
}




per come e' fatto il datapath giocando con

    instruction.opcode.mode_rs2  = is_immediate_data;
    instruction.opcode.mode_rs2  = is_reg_contents;

    instruction.opcode.is_pre_EA = True;
    instruction.opcode.is_pre_EA = False;

si possono ottenere features diverse, pero' va istruito il context della fsm del sequenziatore dicendole esattamente cosa deve fare allo step tal dei tali
Il simulatore esegue codice C o sbaglio?

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #111 il: 29 Gennaio 2014, 23:50:47 »
Non so cosa sono le alee

sono condizioni critiche in cui i dati o i controlli si accavallano tra i vari stadi che cercano di processare in pipe istruzioni in parallelo.
OK, adesso è chiaro, grazie.
Citazione
Se ti studi il modello multi ciclo del MIPS in forma pipelined trovi esempi molto molto mirati che fanno vedere bene tutti i casini che ci sono e come venirne fuori. Alcune soluzioni sono semplici, altre di meno, il punto e' che in quel modello didattico e' tutto documentato e che quanto racconta non e' solo roba didattica ma faccende che purtroppo tocca risolvere nel real world (con altri casini al seguito)
Al momento purtroppo non ho tempo. Ma eventualmente dove potrei trovare questo materiale?
Citazione
Bisogna vedere cosa s'intende per complesso

qualsiasi cosa che abbia bisogno di due dipendenze o di accessi in memoria e' complessa perché impiega più di uno stadio con problemi di dipendenza sul cammino dei dati.

qualsiasi cosa che sia processabile in parallelo con tutte le informazioni che servono gia' presenti e senza alcun accesso alla memoria e' definita semplice, ovvero consumabile in uno solo stadio senza che serva alcun segnale di controllo con impatti sugli stati vicini, p.e. per fermarli per evitare alee.
Perfetto.
Citazione
Il simulatore esegue codice C o sbaglio?

Il simulatore e' scritto in C, quello che vedi e' il come vengono descritti i segnali di datapath per ogni singola istruzione, nell'esempio vedi la bra condizionata, ovvero if (reg[rs]!=0) { PC=reg[ra]+imm32; } else {  PC = PC + sizeof(instruction); }

E' scritto in quel modo il simulatore perche' funge da bozza per un successivo simulatore a micro codice dove tutti i segnali che ora vedi con orizzonte temporale piatto avranno invece una profondità nel tempo, ovvero saranno attivati solo nello step giusto e di competenza, questo significa scrivere appunto micro codice, ovvero istruire in modo preciso le uscite dei vari stati che compongono la macchina a stati che dirige la baracca.
Adesso capisco. Prima pensavo che fosse un'inutile complicazione, perché quando scrivi un emulatore riproduci il funzionamento "grezzo" dell'istruzione, senza scendere così maniacalmente nei dettagli di bassissimo livello.

Qui, invece, stai scrivendo un simulatore, che è ben diverso, visto che deve riprodurre perfettamente tutti questi elementi.
Citazione
p.s.
il simulatore ha messo in evidenza un primo casino da risolvere, ovvero sequenzializzando  --(an) ha bisogno di un precalcolo di an--,
Beh, è simile al calcolo dell'indirizzamento in una modalità che prevede un registro base e un altro valore (costante o altro registro).
Citazione
e ha bisogno di una successiva fase di aggiornamento del registro an, (an)++ non ha bisogno del precalcolo pero' deve aggiornare an, e la fase finale del precedente datapath prevedeva solo una reg write quindi ho dovuto aggiungere una seconda fase per aggiornare anche an, tutto questo porta all'aggiunta di due stadi, uno di calcolo pre_EA, l'altro di aggiornamento registro, oltre al segnale di di pre_EA e di update_ra
Aspetta, l'aggiornamento del registro potresti farlo in parallelo a qualche altro stadio. Ad esempio nello stesso stadio che prevede il caricamento di un dato dalla memoria. Così risparmi un ciclo di clock.
ho gia' una mini alu + MUX che si preoccupa dell'EA

Codice: [Seleziona]

    EA1 = cpu_datapath.source2;
    EA2 = cpu_datapath.source2 + cpu_datapath.source3;


il mux ha per segnale di controllo cpu_datapath.is_pre_EA

cio' rende possibile avere per EA
- una costante immediata + contenuto registro (compreso R0 che e' sempre zero)
- il contenuto di un registro + size
- il contenuto di un registro - size

un altro circuito poi si preoccupa di salvarsi EA2 per darlo in pasto al reg_pool se e' abilitato il segnale update_ra

ovvero ho tutte le modalità di indirizzamento che volevo avere
1) costante immediata 32bit
2) a registro
3) a registro + offset
4) a registro pre incementato/decrementato
5) a registro post decrementato/incrementato

e per ora mi fermo qui
Visto. Beh, è un'ottima base: hai le modalità più diffuse. Ti manca soltanto quella con indice scalato, e dovresti essere a posto. Vedo, però, che finora hai una somma di due soli operandi; ci sono problemi per realizzarne una con 3?
questo documento Altera racconta come fare moltiplicazioni veloci, anche in 3 colpi di clock.
Intanto l'ho scaricato. Me lo smazzo dopodomani, perché adesso mi fiondo a letto, e domani sarà una giornata molto lunga (lavoro + corso di tedesco).

Guten Nacht.

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #112 il: 30 Gennaio 2014, 21:58:30 »
Per il modello didattico del MIPS R1K, ma in generale per un approccio RISC purista, ti consiglerei

Citazione
Struttura e progetto dei calcolatori - Patterson Hennessy, Zanichelli

E' un book usato nei corsi di architetture1, lo trovi anche in italiano (ai mie tempi era solo in inglese), ci sono capitolo che partendo dal multi ciclo arrivano alla implementazione pipelined mettendo in evidenza casini e possibili soluzioni, inoltre ci sono capitolo che mostrano la macchina a stati che governa la baracca, come sintetizzarla, e come si arriva (o meglio come si e' arrivati in passato, per semplificarsi la vita) al micro codice.
E poteva essere altrimenti? Il Patterson. La bibbia di processori e non solo.

Lo conosco di fama, ma ho cercato in giro, e purtroppo non è disponibile in forma elettronica. Ormai ho un Kindle Paperwhite, e vorrei poterlo sfruttare. :P
Qui, invece, stai scrivendo un simulatore, che è ben diverso, visto che deve riprodurre perfettamente tutti questi elementi.

Altrimenti il rischio di pensare che a livello ISA si possa fare qualsiasi cosa, p.e. mov.l (r1)+,(r2)+,
Dopo questo lungo thread comincio a capire perché un'istruzione come questa, che a me fa semplicemente impazzire di gioia, possa creare non pochi problemi: ben due register update, e una load e una store in una sola istruzione, sono LEGGERMENTE pesanti da implementare in una pipeline.

Però diciamo che la load e la store non dovrebbero creare problemi, perché un'istruzione di load in un RISC deve prevedere necessariamente uno stadio di lettura, mentre una di store deve prevedere uno stadio di write-back. Dunque un'istruzione in grado di fare allo stesso tempo sia una load sia una store, non dovrebbe creare problemi: semplicemente utilizzerà entrambi gli stadi dedicati anziché usarne soltanto uno alla volta, come avviene nel nel caso di soltanto una load o una store.

Il casino è rappresentato dai due update, senz'altro. In teoria un update dovrebbe essere "gratis", perché andrebbe indirizzato nello stadio di write-back, che deve poter gestire o lo store di un registro oppure di una locazione di memoria, Ma non credo ci siano problemi nel prevedere la possibilità di effettuare entrambe le cose, visto che si tratta di operazioni indipendenti. E' lo store dell'altro registro a creare problemi...
Citazione
e' forte e rischia poi di non starci fisicamente in una implementazione semplice. Lavorando in retroazione, tra ISA e sua implementazione invece si vedono i casini e si corre subito ai ripari.

L'obiettivo e' semplificare la baracca attorno al datapath!
Chiarissimo e comincio a capire le problematiche implementative.

Una curiosità, ma una mini-ALU dedicata per il calcolo di EA come -(Rn), (Rn)+ e la più complessa (Rn, Rm * Size, Offset32), quanto costa in termini di "spazio" (risorse; LE, ALM, ecc.) occupato? Si mangia molto?
Citazione
Aspetta, l'aggiornamento del registro potresti farlo in parallelo a qualche altro stadio. Ad esempio nello stesso stadio che prevede il caricamento di un dato dalla memoria. Così risparmi un ciclo di clock.

Il modello prevede che tutto il register pool sia aggiornato alla fine, stadio write_back, anticipare la scrittura e' possibile pero' costa un sacco di stati alla fsm per gestire la faccenda, molto + stati che quanti ne servano in uno stadio in coda al reg_pool write dove fondamentalmente si aggiunge una scrittura.

Fino a che non approccio pipelined per me il problema non si pone, ovvero non ho alee, i problemi si farebbero sentire eccome appena si prova ad eseguire istruzioni in parallelo perché se nello stadio register pool read viene letto an_value=reg[an], e poi viene successivamente elaborato an_value+=offset questo valore verra' scritto nel register pool solo alla fine, nello stadio write_back reg[an]=an_value, il che significa che se una istruzione in coda a quella in esecuzione cerca di leggere an si troverà un valore non aggiornato, ovvero non addizionato di +offset.

Per fare come suggerisci serve anticipare la write, questa faccenda pero' costa degli stati per gestire la scrittura, questi stati nello stadio write back ci sono gia', spostare la scrittura in altri stadi invece costa il doverli replicare.
Chiarissimo. In questo caso parliamo di un'istruzione complessa, poiché viene introdotta una dipendenza fra le due istruzioni presenti nella pipeline.
Citazione
In definitiva: fattibilissimo, ma va contro i miei obiettivi di ridurre al minimo gli stati della fsm che dirige la baracca.

Penso che ci debba essere un compromesso, la coperta e' sempre corta: o ti accontenti di una cariola che consuma un colpo di clock in + per farti avere degli stati in meno nella fsm che la dirige, o cerchi di eliminare quel colpo di clock in più aumentando la complessità e gli stati della fsm.

A me la scelta, sarebbe bello trovare la formula della moglie ubriaca con la botte piena  ;D
Ma nessuno t'impedisce d'implementare la tua architettura non-pipelined per il momento, e poi sperimentare per vedere di farla diventare pipelined. E poi, ancora, super-pipelined in-order. E infine... out-of-order... a 2 vie, poi a 3 vie, a 4, e così via fino a 7 (quanto l'A7 di Apple).

Ne hai di cose da sperimentare. Immagina la goduria e quante "belle" FSM salteranno fuori. ;D ;D ;D

Offline cdimauro

  • Human Debugger
  • *****
  • Post: 4291
  • Karma: +7/-95
    • Mostra profilo
Re:Retargetable C Compiler
« Risposta #113 il: 01 Febbraio 2014, 09:13:55 »
Domani ti rispondo, adesso mi compiaccio della parte di simulatore che tiene conto del dynamic bus sizing (1) e dalla generazione dei cicli bus distinguendo tra OPP_SIZE e BUS_SIZE, questa ultima e' figlia di come il device verso cui/da cui la CPU intende fare IO (push/pop, IO_read, IO_store) segnala il suo size pilotando i segnali {size0,size1} per dire che può accettare cicli asincroni {byte, word, long}; ne tengo conto per essere flessibile e sopratutto per considerare anche la casistica del bus error dovuto al controllo bad alignment quando la fsm del bus trova una addr inadeguato.
Strano che tu abbia introdotto il dynamic bus sizing. Ma non hai una cache per gestire gli accessi alla memoria?
Citazione
In quel caso completo il ciclo alzando una flag di exception che verra' poi presa in considerazione in due punti:
1) nel registro exception cause, c'e' un bit apposito per il bus error di tipo bad alignment, piuttosto che device time out (se fallisce il dtack), piuttosto che hw failure se qualcuno lo dice esplicitamente
2) a prescindere dalla causa se una sola flag di exception e' sollevata all'update del PC viene poi caricato EPC, ovvero si passa a 0x0000000 dove ha inizio una unica exception routine che a quel punto interroga il registro cause per capire cosa e' successo.

Versione molto semplificata, per ora, anche perché un altro bel casino sono le exceptions, sopratutto come gestile. Sicuramente non voglio le fsm vettorizzate o autovettorizzate del 68K perché sono troppo complicate!

La cosa forte e' che posso sfruttare una mask_cause associata a cause che mi permette di non fare alcun if per capire cosa e' successo, ovvero sfrutto la mask come offset per una cosa tipo bra mask_cause(EPC) -> EPC+=mask_cause

mask_cause spaia di poco, diciamo che mask_cause = cause << 8, pero' facendo in quel modo poi a EPC+mask_cause infilo una BRA verso la routine specifica caso per caso


quindi il giro e'
1) EPC = 0x00000000 con cause=1,2,3,4,5,….
2) jump EPC=EPC+(cause<<4)
3) ….
4) RTI
Hai fatto bene. Le eccezioni vettorizzate sono troppo complicate da gestire. Anch'io preferisco un modello nettamente più semplice, e simile a quello che hai introdotto tu.
Citazione
(1) l'ho preso  dalla doc Motorola 68020, il MIPS non prevede una cosa del genere, quantomeno non la mera CPU, ovvero se ne deve occupare una logica esterna e segnalare alla CPU una hardware exception da mappare poi in qualche modo come bad alignment
Lo ricordo bene. 8)
Ah, ovviamente sotto exception il context switch e' istantaneo poiché servito dall'hw che in questo caso da taskID=0, quindi cambia del tutto l'indice di accesso ai registri, esattamente come nelle shadowed registers machines.
Visto che li hai implementati così i registri, è un'eccellente soluzione. :)

Tags: