2011-09-20 8 views
15

Sto progettando un computer seriale TTL e non riesco a scegliere un'architettura più adatta al backend del compilatore LLVM (voglio essere in grado di eseguire qualsiasi software C++ lì). Non ci saranno MMU, né moltiplicazione/divisione, né stack hardware, né interruzioni.Scelta dell'architettura CPU per LLVM/CLANG

ho 2 opzioni principali: memoria

1) a 8-bit, 8-bit ALU, registri a 8 bit (~ 12-16). Larghezza dell'indirizzo di memoria 24 bit. Quindi avrò bisogno di usare 3 registri come IP e 3 registri per qualsiasi posizione di memoria.

Inutile dire che qualsiasi calcolo di indirizzi sarebbe un dolore puro da implementare nel compilatore.

2) Memoria a 24 bit, ALU a 24 bit, registri a 24 bit (~ 6-8). Memoria piatta, bella. Gli svantaggi sono che a causa della natura seriale del progetto, ogni operazione richiederebbe 3 volte più orologi, anche se stiamo operando su alcuni booleani. La larghezza dei dati della memoria a 24 bit è costosa. Ed è più difficile da implementare nell'hardware in generale.


La domanda è: l'hardware Pensi implementare tutte le funzioni C++ su questo 8-bit, stack-meno basa è possibile, o ho bisogno di avere l'hardware più complesso di avere il codice generato di ragionevole velocità qualità &?

+0

Questa potrebbe essere una domanda ingenua, ma perché è necessario implementare tutte le funzionalità di C++? Non puoi semplicemente scrivere una nuova architettura di destinazione LLVM e Clang compilerà C++ senza problemi? –

+0

@Dan Cecile Questo è esattamente quello che sto per fare. Ma si può vedere che scrivere backend LLVM per CPU a 8 bit con spazio di memoria a 24 bit potrebbe essere un po 'non banale. – BarsMonster

+0

Quindi hai 4 parti; capire come implementare il [linguaggio assembly LLVM] (http://llvm.org/docs/LangRef.html) funziona con il tuo hardware, copiando e adattando un [target LLVM esistente] (https://github.com/earl/llvm-mirror/tree/master/lib/Target) per lavorare con il tuo hardware, cercando di capire come ottenere Clang a [generare un bytecode LLVM a 8 bit] (http://clang.llvm.org/doxygen/classclang_1_1TargetInfo.html) ([le dimensioni del puntatore sono configurabili] (http://clang-developers.42468.n3.nabble.com/Re-targeting-clang-to-a-newewarchitecture-tp761920p762813.html)), quindi modificare l'hardware in essere più adatto. –

risposta

18

I secondo il suggerimento di utilizzare LCC. L'ho usato in questo progetto RISC homebrew a 16 bit: http://fpgacpu.org/xsoc/cc.html.

Non penso che dovrebbe fare molta differenza se si costruisce la variante a 8 bit e si usano 3 add-with-carry per incrementare l'IP, o la variante a 24 bit e fare tutto in hardware. Puoi nascondere la differenza nel tuo assemblatore.

Se si guarda il mio articolo qui sopra, o una CPU ancora più semplice qui: http://fpgacpu.org/papers/soc-gr0040-paper.pdf vedrai che non hai davvero bisogno di molti operatori/istruzioni per coprire il Repeaterire intero C. In effetti esiste una utility lcc (ops) per stampare l'operatore minimo impostato per una data macchina.

Per ulteriori informazioni si veda il mio articolo sul porting LCC per una nuova macchina qui: http://www.fpgacpu.org/usenet/lcc.html

Una volta avevo portato LCC, ho scritto un assemblatore, ed è sintetizzato una grande repertorio delle istruzioni da quelli di base.Per esempio, la mia macchina ha avuto carico-byte senza segno, ma non carico byte-firmato, così ho emesso questa sequenza:

lbs rd,imm(rs) -> 
    lbu rd,imm(rs) 
    lea r1,0x80 
    xor rd,r1 
    sub rd,r1 

Quindi penso che si può ottenere con questo min copertina di operazioni:

registers 
    load register with constant 
    load rd = *rs 
    store *rs1 = rs2 
    + - (w/ w/o carry) // actually can to + with - and^
    >> 1     // << 1 is just + 
    &^     // (synthesize ~ from ^, | from & and ^) 
    jump-and-link rd,rs // rd = pc, pc = rs 
    skip-z/nz/n/nn rs  // skip next insn on rs==0, !=0, <0, >=0 

Ancora più semplice è non avere registri (o registri di sfocatura equivalenti con memoria - tutti i registri hanno un indirizzo di memoria).

Impostare un registro per SP e scrivere la funzione prolog/epilog handler nel compilatore e non sarà necessario preoccuparsi delle istruzioni di stack. C'è solo il codice per memorizzare ciascuno dei registri di salvataggio del callee, regolare l'SP in base alla dimensione del frame e così via.

Interruzioni (e ritorno da interrupt) sono semplici. Tutto ciò che devi fare è forzare un'istruzione Jump-and-Link nel registro delle istruzioni. Se si sceglie il modello di bit affinché sia ​​qualcosa come 0 e si inseriscano gli indirizzi giusti nel registro sorgente rs (specialmente se è r0), può essere fatto con un ingresso di reset flip-flop o un extra 0 e cancello. Io uso un trucco simile nel secondo foglio sopra.

Progetto interessante. Vedo che è in corso un concorso TTL/7400 e stavo pensando a quanto una macchina potesse essere semplice e sarebbe stato ingannevole aggiungere alla macchina una SRAM asincrona da 32 KB o 128 KB per contenere codice e dati.

In ogni caso, felice hacking!

p.s.

1) Sarà necessario decidere la dimensione di ciascun tipo di integrale. Puoi certamente creare char, short, int, long, long long, ecc. Della stessa dimensione, una parola 24b, se lo desideri, anche se non sarà conforme in intervalli di rappresentazione min.

2) E anche se mi sono concentrato su lcc qui, mi stavi chiedendo del C++. Per prima cosa raccomando di persuadere C. Una volta che hai capito le cose per C, inclusi gli operatori *, /,% nel software, ecc., Dovrebbe essere più trattabile passare al C++ completo, sia in LLVM che in GCC. La differenza tra C e C++ è "solo" i vtables extra e le tabelle RTTI e le sequenze di codice (interamente compilate dal ripetitore di operatore intero C primitivo) richieste per gestire chiamate di funzioni virtuali, puntatore alla dereferenziazione di membri, stringhe dinamiche, costruttori statici, eccezione gestione, ecc.

+2

p.s. Stavo pensando. Dovresti costruire la variante 24b preferibilmente alla variante 8b. Non è perché le operazioni di dati più ampie richiedono più istruzioni ciascuna, piuttosto perché è veramente necessario un concetto di spazio di indirizzamento 24b lineare per istruzioni e dati. Mentre è possibile sintetizzare un carico di 24b su 3 carichi da 8b, è necessario un indirizzo 24b (e un incremento dell'indirizzo di 24 bit) per gestire il recupero dell'istruzione, il contatore del programma e l'incremento del PC nell'hardware. Non è chiaro come sia possibile gestire programmi più grandi di 256 istruzioni se si hanno solo indirizzi 8b. –

1

A mio parere, l'hardware senza stack è già poco adatto al codice C e C++. Se hai chiamate di funzioni nidificate, dovrai comunque emulare uno stack nel software, che ovviamente è molto più lento.

Durante il percorso senza stack, probabilmente allocare la maggior parte delle variabili come "statico" e non avere funzioni di rientro. In questo caso, le modalità di indirizzamento in stile 6502 possono essere efficaci. Si potrebbe ad esempio avere questi modi di indirizzamento:

  1. indirizzo immediata (24bit) come parte del codice operativo
  2. indirizzo immediata (24bit) più registro indice (8 bit)
  3. accesso indiretto: indirizzo 24bit immediato alla memoria, che contiene l'indirizzo effettivo
  4. Accesso indiretto: indirizzo a 24 bit in memoria, registro indice a 8 bit aggiunto al valore dalla memoria.

Le modalità di indirizzo sopra descritte consentirebbero un accesso efficiente a matrici, strutture e oggetti assegnati a un indirizzo costante (allocazione statica). Sarebbero meno efficienti (ma ancora utilizzabili) per oggetti dinamicamente e impilati.

Si potrebbe anche ottenere qualche beneficio dal suo disegno di serie: di solito il + Oltre 24 bit bit 8 non prende 24 cicli, ma è possibile invece di corto circuito l'aggiunta quando riporto è 0.

Invece di mappatura l'IP come registri direttamente, è possibile consentire di cambiarlo solo tramite istruzioni goto/branch, utilizzando le stesse modalità di indirizzo di cui sopra. I salti negli indirizzi dinamicamente calcolati sono piuttosto rari, quindi ha più senso fornire l'intero indirizzo a 24 bit direttamente nell'opcode.

Penso che se si progetta la CPU con attenzione, è possibile utilizzare molte funzionalità di C++ in modo abbastanza efficiente. Tuttavia, non aspettarti che qualsiasi codice C++ casuale possa essere eseguito velocemente su una CPU così limitata.

+0

Sì, il codice stackless è solo inutile: voglio ancora poter eseguire la crosscompile per il codice esistente. Quindi dovrò usare lo stack emulato. – BarsMonster

+1

È possibile implementare uno stack hardware; richiede solo altre due istruzioni e l'incremento/decremento è abbastanza semplice e veloce in un'architettura seriale. – jpa

1

L'implementazione è certamente possibile, ma dubito che sarà utilizzabile (almeno per codice C++).Come già notato, il primo problema è la mancanza di stack. Successivamente, un sacco di C++ si affida pesantemente all'allocazione dinamica della memoria, anche le strutture "interne" del C++ sono piuttosto grandi.

Così, come a me sembra, sarà meglio, se si:

  1. sbarazzarsi di C++ requisito (o almeno, limitatevi a un sottoinsieme)
  2. Usa 24 bit, non 8 bit per tutto (per registri pure) pila hardware
  3. Add
+0

Bene, implementare il sottoinsieme è più difficile dell'intero C++. In quest'ultimo caso ho solo bisogno di scrivere clang/llvm backend. Perché l'assenza di hardware impone un problema così critico? Il software non consente di fare lo stesso? – BarsMonster

+0

Se non ti interessa nessuna prestazione - allora stai bene, sì :) –

3

IMHO, è possibile per c compilatore. non sono sicuro per C++, però.

LLVM/Clang potrebbe essere scelta difficile per il calcolatore 8bit,

Invece, primo tentativo lcc, quindi secondo LLVM/etc, HTH.

Bill Buzbee riescono a retarget compilatore LCC per la sua Magic-1 (noto come homebrewcpu).

Anche se la progettazione hardware e la costruzione di Magic-1 di solito ricevono più attenzione, la maggior parte del progetto (di gran lunga) ha sviluppato/portato il software. A tal fine, ho dovuto scrivere un assemblatore e un linker da zero, eseguire il retarget di un compilatore C, scrivere e portare le librerie C standard, scrivere un sistema operativo semplificato e poi portarne uno più sofisticato. È stata una sfida, ma divertente. Suppongo di essere un po 'contorto, ma mi capita di divertirmi nel debug di problemi difficili. E, quando il bug che stai cercando di rintracciare potrebbe coinvolgere uno o più dei seguenti: difetto di progettazione hardware, cavo allentato o rotto, chip TTL allentato o cattivo, bug dell'assemblatore, bug del linker, bug del compilatore, bug della libreria di runtime C o infine un bug nel programma in questione ci sono molte opportunità di divertimento. Oh, e anche io non ho il lusso di incolpare gli insetti su qualcun altro.

Sono continuamente stupito dal fatto che quella dannata cosa funzioni, e molto meno che funzioni.

-1

Non è possibile eseguire "qualsiasi" codice C++ lì. Ad esempio fork(), system(), ecc. Tutto ciò che chiaramente si basa sugli interrupt, ad esempio. Puoi fare una lunga strada lì, certo. Ora intendi qualsiasi programma che può/è stato scritto in C++ o stai limitando te stesso alla sola lingua e non alle librerie che sono comunemente associate a C/C++? La stessa lingua è una regola molto più facile da vivere.

Penso che la domanda/risposta più semplice, è, perché non provare? Cosa hai provato fino ad ora? Si potrebbe sostenere che l'x86 sia una macchina a 8 bit, senza riguardo per l'allineamento e molte istruzioni a 8 bit. msp430 è stato portato su llvm per mostrare quanto facilmente e rapidamente possa essere fatto, mi piacerebbe vedere quella piattaforma con un supporto migliore (non dove i miei punti di forza si trovano altrimenti lo farei) una piattaforma a 16 bit. no mmu. ha uno stack e gli interrupt sicuri, non devi usarli e se rimuovi le regole della libreria, allora cosa rimane che ha bisogno di un interrupt?

Guarderei llvm ma nota che la documentazione prodotta mostra quanto sia facile portarlo, sia datato e sbagliato e in pratica devi capire da solo dalle fonti del compilatore. llc ha un libro, noto per questo, non ottimizzato.Le fonti non si adattano bene ai computer moderni, devono sempre tornare indietro nel tempo per usarlo, ogni volta che ci vado vicino dopo una serata, solo cercando di costruirlo come mi arrendo. vbcc, semplice, pulito, documentato, non ostile ai processori più piccoli. È C++, non ricorda. Di tutti loro è il più semplice ottenere un compilatore attivo e funzionante. Di tutti loro LLVM è il più attraente e più utile quando tutto è stato detto e fatto. non andare vicino a gcc o addirittura pensarci, nastro adesivo e filo di bloccaggio dentro tenerlo insieme.

Hai già inventato il set di istruzioni? hai ancora un simulatore e un assemblatore? Guarda lsasim a Github per trovare il mio set di istruzioni. Puoi scrivere un backend llvm per il mio come pratica per il tuo ... ghigno ... (il mio backend vbcc è orribile, ho bisogno di ricominciare) ...

Devi avere qualche idea di come l'alto livello sarà implementato ma devi davvero iniziare con un set di istruzioni e un simulatore di set di istruzioni e un assemblatore di qualche tipo. Quindi avvia la conversione manuale del codice C/C++ in assembly per il tuo set di istruzioni, che dovrebbe essere abbastanza veloce da eseguire "posso farlo senza stack", ecc. In questo processo definisci la tua convenzione di chiamata, implementa più codice C/C++ a mano usando la tua convenzione di chiamata. POI scavare in un compilatore e fare un back-end. Penso che dovresti considerare il vbcc come un trampolino di lancio, quindi dirigiti verso LLVM se sembra che (l'isa) funzionerà.