2011-09-20 17 views
15

Estoy diseñando una computadora serie TTL, y estoy luchando para elegir una arquitectura más adecuada para el compilador de fondo LLVM (quiero poder ejecutar cualquier software C++ allí). No habrá MMU, no habrá multiplicación/división, no habrá pila de hardware, no habrá interrupciones.Elección de arquitectura de CPU para LLVM/CLANG

tengo 2 opciones principales: la memoria

1) de 8 bits, 8 bits de la ALU, registros de 8 bits (~ 12-16). Ancho de dirección de la memoria 24 bit. Entonces necesitaré usar 3 registros como IP y 3 registros para cualquier ubicación de memoria.

Huelga decir que cualquier cálculo de dirección sería un verdadero dolor para implementar en el compilador.

2) memoria de 24 bits, 24U de ALU, registros de 24 bits (~ 6-8). Memoria plana, agradable. Los inconvenientes son que debido a la naturaleza serial del diseño, cada operación demoraría 3 veces más relojes, incluso si estamos operando en algunos booleanos. El ancho de datos de la memoria de 24 bits es costoso. Y es más difícil de implementar en hardware en general.


La pregunta es: hardware ¿Cree que la aplicación de todos ++ características c en este 8 bits, apilar-less basa es posible, o tengo que tener un hardware más complejo para tener código generado de razonable velocidad de la calidad &?

+0

Esto puede ser una pregunta ingenua, pero ¿por qué tiene que implementar todas las características de C++? ¿No puede simplemente escribir una nueva arquitectura de destino LLVM y Clang compilará C++ sin problemas? –

+0

@Dan Cecile Eso es exactamente lo que voy a hacer. Pero puede ver que escribir backend LLVM para CPU de 8 bits con espacio de memoria de 24 bits podría ser un poco no trivial. – BarsMonster

+0

Entonces tiene 4 partes; averiguar cómo implementar el [lenguaje ensamblador LLVM] (http://llvm.org/docs/LangRef.html) funciona con su hardware, copiando y adaptando un [destino LLVM existente] (https://github.com/earl/llvm-mirror/tree/master/lib/Target) para trabajar con su hardware, descubriendo cómo obtener Clang para [generar bytecode LLVM de 8 bits] (http://clang.llvm.org/doxygen/classclang_1_1TargetInfo.html) ([los tamaños del puntero son configurables] (http://clang-developers.42468.n3.nabble.com/Re-targeting-clang-to-a-new-architecture-tp761920p762813.html)), luego cambia el hardware a ser más adecuado. –

Respuesta

18

En segundo lugar, sugiero usar LCC. Lo usé en este proyecto casero RISC de 16 bits: http://fpgacpu.org/xsoc/cc.html.

No creo que deba hacer mucha diferencia si construye la variante de 8 bits y usa 3 add-with-carries para incrementar IP, o la variante de 24 bits y lo hace todo en hardware. Puede ocultar la diferencia en su ensamblador.

Si miras mi artículo anterior, o una CPU aún más simple aquí: http://fpgacpu.org/papers/soc-gr0040-paper.pdf verás que realmente no necesitas tantos operadores/instrucciones para cubrir el entero del repertorio C. De hecho, hay una utilidad lcc (ops) para imprimir el operador mínimo establecido para una máquina determinada.

Para obtener más información, véase mi artículo en portar LCC a una nueva máquina aquí: http://www.fpgacpu.org/usenet/lcc.html

Una vez que había portado LCC, escribí un ensamblador y se sintetizó un repertorio más amplio de instrucciones de los básicos.Por ejemplo, mi máquina tenía la carga de bytes sin signo, pero no firmado carga de bytes, por lo que emite esta secuencia:

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

Así que creo que se puede llegar a funcionar con esta cubierta min de operaciones:

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 

Aún más simple es no tener registros (o de forma equivalente, desenfocar los registros con la memoria, todos los registros tienen una dirección de memoria).

Separe un registro para SP y escriba la función prolog/epilog handler en el compilador y no tendrá que preocuparse por las instrucciones de la pila. Solo hay un código para almacenar cada uno de los registros de guardado de llamadas, ajustar el SP por el tamaño del marco, y así sucesivamente.

Las interrupciones (y el retorno de las interrupciones) son sencillas. Todo lo que necesita hacer es forzar una instrucción de salto y enlace en el registro de instrucciones. Si elige el patrón de bits para que sea algo así como 0 y coloque las direcciones correctas en el registro de origen rs (especialmente si es r0), se puede hacer con una entrada de restablecimiento de flip-flop o una fuerza adicional para 0 y puerta. Utilizo un truco similar en el segundo documento anterior.

Proyecto interesante. Veo que un concurso TTL/7400 está en marcha y yo estaba pensando en cuán simple podría ser una máquina para salirse con la suya y sería una trampa incorporar una SRAM asíncrona de 32 KB o 128 KB a la máquina para contener el código y los datos.

De todos modos, feliz piratería!

p.s.

1) Querrá decidir qué tan grande es cada tipo integral. Ciertamente puede hacer char, short, int, long, long long, etc. del mismo tamaño, una palabra 24b, si lo desea, aunque no será compatible en rangos de representación mínima.

2) Y aunque me centré en lcc aquí, preguntabas por C++. Recomiendo persuadir C primero. Una vez que tenga las cosas resueltas para C, incluidos *, /,% operadores en software, etc., debería ser más manejable pasar a C++ completo, ya sea en LLVM o GCC. La diferencia entre C y C++ es "solo" las tablas y secuencias de códigos RTTI adicionales (completamente desarrolladas en el primitivo operador de repertorio entero C) requeridas para manejar llamadas a funciones virtuales, puntero a la desreferencia de miembros, moldes dinámicos, constructores estáticos, excepción manejo, etc.

+2

p.s. Yo estaba pensando. Debe construir la variante 24b en preferencia a la variante 8b. No se debe a que las operaciones de datos más amplias requieran múltiples instrucciones cada una, más bien porque realmente se necesita un concepto de un espacio de direcciones 24b lineal para las instrucciones y los datos. Si bien puede sintetizar una carga de 24b de cargas de 3 8b, necesita una dirección 24b (y un incremento de direcciones 24b) para manejar la recuperación de instrucciones, el contador de programas y el incremento de PC en el hardware. No está claro cómo podría manejar programas de más de 256 instrucciones si solo tiene 8b direcciones. –

1

En mi opinión, el hardware sin pila ya no es adecuado para el código C y C++. Si tiene llamadas de función anidadas, tendrá que emular una pila en el software de todos modos, que por supuesto es mucho más lenta.

Cuando va por la ruta sin apilamiento, probablemente asigne la mayoría de las variables como "estáticas" y no tenga funciones de reentrada. En este caso, los modos de direccionamiento al estilo 6502 pueden ser efectivos. Por ejemplo, puede tener estos modos de direccionamiento:

  1. dirección de inmediato (24 bits) como parte del código de operación
  2. dirección de inmediato (24 bits), además del registro de índice (8 bits)
  3. acceso indirecto: la dirección de 24 bits inmediata a la memoria, que contiene la dirección real
  4. Acceso indirecto: dirección de 24 bits en la memoria, registro de índice de 8 bits agregado al valor de la memoria.

Los modos de dirección descritos anteriormente permitirían el acceso eficiente a matrices, estructuras y objetos asignados en una dirección constante (asignación estática). Serían menos eficientes (pero aún usables) para objetos dinámicos y asignados por pila.

También podrían obtener algún beneficio de su diseño de serie: por lo general el 24 bits + 8 bits además no toma 24 ciclos, pero en su lugar puede cortocircuito cuando la adición de transporte es 0.

En lugar de mapeo el IP como registros directamente, usted podría permitir cambiarlo solo a través de las instrucciones goto/branch, usando los mismos modos de dirección que arriba. Los saltos en direcciones calculadas dinámicamente son bastante raros, por lo que tiene más sentido dar la dirección completa de 24 bits directamente en el código de operación.

Creo que si diseña la CPU con cuidado, puede utilizar muchas funciones de C++ de manera bastante eficiente. Sin embargo, no espere que cualquier código aleatorio de C++ se ejecute rápidamente en una CPU tan limitada.

+0

Sí, el código apilado es inútil: aún quiero poder comparar el código existente para él. Entonces tendré que usar la pila emulada. – BarsMonster

+1

Podría implementar una pila de hardware; requiere solo dos instrucciones más y el incremento/disminución es bastante rápido y fácil en una arquitectura en serie. – jpa

1

La implementación es ciertamente posible, pero dudo que sea utilizable (al menos para el código C++).Como ya se señaló, el primer problema es la falta de pila. A continuación, el grupo de C++ se basa en gran medida en la asignación de memoria dinámica, también las estructuras "internas" de C++ son bastante grandes.

Así que, como me parece, será mejor, si usted:

  1. deshacerse de C++ requisito (o al menos, se limita a un subconjunto)
  2. uso de 24 bits, no se 8 bits para todo (para los registros, así pila)
  3. agregar hardware
+0

Bueno, la implementación del subconjunto es más difícil que todo C++. En este último caso, solo necesito escribir clang/llvm backend. ¿Por qué la ausencia de hardware acumula un problema tan crítico? ¿El software uno no permite hacer lo mismo? – BarsMonster

+0

Si no te importa cualquier actuación, entonces estás bien, sí :) –

3

en mi humilde opinión, es posible que el compilador c. Aunque no estoy seguro de C++.

LLVM/ruido metálico podría ser difícil elección para el ordenador de 8 bits,

En cambio, en primer lugar tratar lcc, luego la segunda llvm/etc, HTH.

Bill Buzbee logran volver a orientar compilador LCC por su Magic-1 (conocido como homebrewcpu).

Aunque el diseño de hardware y la construcción de Magic-1 generalmente recibe la mayor atención, la mayor parte del proyecto (por el momento) ha estado desarrollando/portando el software. Para este fin, tuve que escribir un ensamblador y un enlazador desde cero, reorientar un compilador de C, escribir y portar las librerías estándar de C, escribir un sistema operativo simplificado y luego portar uno más sofisticado. Ha sido un desafío, pero divertido. Supongo que estoy algo torcido, pero disfruto de la depuración de problemas difíciles. Y, cuando el error que está tratando de rastrear podría involucrar uno o más de: falla de diseño de hardware, cable suelto o roto, chip TTL suelto o malo, error de ensamblador, error de enlazador, error de compilación, error de biblioteca de tiempo de ejecución de C o finalmente un error en el programa en cuestión, hay muchas oportunidades para divertirse. Ah, y tampoco tengo el lujo de culpar a los errores de nadie más.

Estoy continuamente sorprendido de que la maldita cosa se ejecute en absoluto, y mucho menos funciona tan bien como lo hace.

-1

No podrá ejecutar "ningún" código C++ allí. Por ejemplo fork(), system(), etc. Cualquier cosa que dependa claramente de interrupciones, por ejemplo. Puedes recorrer un largo camino, seguro. ¿Te refieres a cualquier programa que pueda/haya sido escrito en C++ o te estás limitando al lenguaje solamente y no a las bibliotecas que comúnmente están asociadas con C/C++? El idioma en sí es una regla mucho más fácil de vivir.

Creo que la pregunta/respuesta más fácil es, ¿por qué no intentarlo? ¿Qué has intentado hasta ahora? Se podría argumentar que el x86 es una máquina de 8 bits, sin importar la alineación y muchas instrucciones de 8 bits. el msp430 fue portado a llvm para mostrar cuán fácil y rápidamente se podía hacer, me gustaría ver esa plataforma con un mejor soporte (no donde mis puntos fuertes radiquen sino lo haría) una plataforma de 16 bits. no mmu. tiene una pila e interrumpe con seguridad, no tiene que usarlos y si elimina las reglas de la biblioteca, ¿qué queda entonces que necesita una interrupción?

Me gustaría ver llvm, pero tenga en cuenta que la documentación producida que muestra qué tan fácil es de puerto, es anticuado e incorrecto y, básicamente, tiene que averiguarlo por su cuenta desde las fuentes del compilador. llc tiene un libro, conocido por eso, no optimizado.Las fuentes no se compilan bien en las computadoras modernas, siempre teniendo que retroceder en el tiempo para usarlas, cada vez que me acerco después de una tarde solo intento construirlas y me da por vencido. vbcc, simple, limpio, documentado, no hostil para procesadores más pequeños. Es C++, no lo recuerdes. De todos ellos, es el más fácil de poner en marcha un compilador. De todos ellos, LLVM es el más atractivo y más útil cuando todo está dicho y hecho. No te acerques a gcc ni siquiera pienses en ello, con cinta adhesiva y alambre de achique dentro para mantenerlo unido.

¿Has inventado ya tu conjunto de instrucciones? ¿Ya tienes un simulador y ensamblador? Busca lsasim en github para encontrar mi conjunto de instrucciones. Puedes escribir un backend de llvm para el mío como práctica para los tuyos ... sonrisa ... (mi backend de vbcc es horrible, necesito empezar de nuevo) ...

Tienes que tener una idea de cómo el alto nivel será implementado pero realmente tiene que comenzar con un conjunto de instrucciones y un simulador de conjunto de instrucciones y un ensamblador de algún tipo. Luego, comience a convertir manualmente el código C/C++ al conjunto para su conjunto de instrucciones, que le guiará rápidamente en "puedo hacerlo sin una pila", etc. En este proceso defina su convención de llamadas, implemente más código C/C++ a mano usando su convención de llamadas. ENTONCES cavar en un compilador y hacer un back-end. Creo que deberías considerar a vbcc como un trampolín, luego dirígete a LLVM si parece que así (el isa) funcionará.

Cuestiones relacionadas