Análisis del chip ACID del Amstrad CPC+/GX4000
Valoración de usuario: / 8
PeorMejor 
Sábado, 16 de Abril de 2011 01:11

Los cartuchos compatibles con la consola Amstrad GX4000 y los ordenadores de la serie CPC+ de Amstrad necesitan un chip "llave" llamado ACID (Amstrad Cartridge Identification Device). Durante el último año colaboré en el análisis de su funcionamiento con la intención de anularlo y dejar la consola desprotegida. Paralelamente, No$Cash estudió el comportamiento del chip en sí y consiguió descifrar el algoritmo que ejecuta. Como resultado, ha sido posible describir el comportamiento del chip en un lenguaje HDL (Verilog) y su posterior implementación en una CPLD.

El ACID es un pequeño integrado con encapsulado DIL de 16 pines. En la CPCWiki hay más información sobre él.

Comenzaré este artículo desde el principio, es decir, desde mis primeros "escarceos" con el chip. La siguiente información se encuentra dispersa en unos cuantos posts que escribí en el hilo "¿Qué hace realmente el chip ACID de los cartuchos" en el foro de Amstrad de MiArroba. Los transcribo en orden temporal.

 

Note to english speakers: I wrote something about my findings at Usenet. Not as verbose as the spanish version, though.

El 13 de Octubre del 2009 comencé con mi pequeña investigación...

Como primera acción, procedí a medir las señales SIN y CCLR en relación al reloj CLK. SIN cambia en el flanco negativo del reloj. CCLR queda a nivel alto.


Señal CLK (arriba y SIN (abajo)


Señal SIN (arriba y CCLR (abajo)

El ACID funciona independientemente del Z80. Si quito el procesador, el ACID sigue generando la señal SIN.

Dado que no saqué mucho en claro viendo SIN y CCLR (No$Cash vio mucho más de lo que yo vi), afronté el problema desde otro punto de vista: ¿qué pasa exactamente en el Amstrad cuando no está el ACID? ¿Qué cambia? Si la influencia del ACID es algo tan trivial como inhibir alguna señal del procesador, dar resets (como el CIC de la NES) o algo por el estilo, quizás fuera más fácil anular ese efecto que tratar de clonar el comportamiento del chip.

Obviamente, si quitas el ACID el cartucho no arranca, pero ¿por qué?

Para responder a esta pregunta, escribí un programa que sólo usa memoria de la EPROM, no usa RAM para almacenamiento temporal, ni usa subrutinas, ni PUSH ni POP ni nada de eso.

El programa inicializa el gate array a modo 2, con colores blanco y negro. Luego se mete en un bucle sin fin, en el cual cambia el color del borde, escribe un patrón de bytes a memoria de pantalla (escribe un byte con todos sus bits a 0 excepto uno, que lo pone a 1. De esa forma puedo averiguar qué bit es el que falla en una memoria y así reemplazarla). En cada vuelta del bucle, el bit que está a 1 se va desplazando de derecha a izquierda para probar todos los bits de un byte. Luego, se hace lo mismo pero en "video inverso", es decir, se escribe un byte con todos sus bits a 1 excepto uno que se pone a 0. Después de rellenar la pantalla, hago sonar el generador de sonidos con un pitido de 1kHz. Luego espero un segundo, y vuelta a empezar con otra vuelta del bucle.

Con la EPROM del Burnin Rubber borrada (por supuesto hice copia de seguridad de su contenido :)) y vuelta a grabar con este mini test, hice las siguientes pruebas:

  1. ACID colocado. Memoria RAM colocada (está en zocalos). El test se ejecuta con normalidad, igual que en el CPC464 donde lo probé inicialmente.
  2. ACID quitado. Memoria RAM colocada. El test se ejecuta. Suena el altavoz, cambia el borde, pero la pantalla no se actualiza. Se queda con un patrón fijo que no es ninguno de los que yo escribo.
  3. ACID colocado. Memoria RAM quitada. El test se ejecuta. Suena el altavoz, cambia el borde. El patrón que escribo a memoria aparece en pantalla, durante un instante, para después desvanecerse. Esto me desconcierta porque este patrón no tiene dónde guardarse, ya que no hay memoria RAM.
  4. ACID quitado. Memoria RAM quitada. Pasa igual que en el caso 3, salvo que el patrón que se ve en pantalla antes de desvanecerse, es el mismo que se ve en el caso 2, así que este comportamiento desconcertante del que he hablado no tiene que ver con el ACID.

En los casos 3 y 4, el desvanecimiento es a blanco (píxeles a 1). Lógico que éste sea el resultado final cuando no hay memoria, ya que es sinómino de "bus flotante" pero me extraña que tarde tanto, y que haya algo así como una persistencia de los valores en los 16K de pantalla, cuando no existen esos 16K.

Lo que está claro es que el acceso a memoria no es igual con ACID y sin ACID. En este último caso, los valores que escribo en RAM (posiciones C000 a FFFF) no los veo, pero puede ser por alguna de estas razones:

  • El ACID no deja al Z80 escribir a memoria: los pulsos de WE aparecen en las memorias, pero es posible que RAS y CAS no sean correctos, o que el bus de direcciones apunte a un sitio diferente.
  • El ACID no deja al Gate Array leer la memoria: habría que mirar el ciclo de lectura de la memoria en ambos casos: con ACID y sin él. Si esta razón es la correcta, entonces seguramente tampoco deje al Z80 leer la memoria, aunque haya escrito correctamente en ella.
  • Para poder ver qué pasa, usé un analizador lógico USB de 32 canales. ¡¡El pobre CPC+ parecía que estaba en la UVI!! Así lo tenía:

    Durante la ejecución del pequeño test, se llena la pantalla con un patrón que difiere cada vez en un bit. En este caso se ve que se ha llenado la pantalla con un patrón de bytes que contiene todos sus bits a 1 excepto uno (el más significativo quizás a juzgar por lo cercano a la izquierda del borde que están las líneas) que está a 0. El color para el pixel apagado es negro, y para el pixel encendido es 1.

    Más concretamente, las pantallas que van apareciendo en el test son consecuencia de llenar la memoria de pantalla (C000 a FFFF) con los siguientes valores, uno en cada vuelta del bucle: 01h, 02h, 04h, 08h, 10h, 20h, 40h, 80h, FEh, FDh, FBh, F7h, EFh, DFh, BFh, 7Fh, y vuelta a empezar con 01h...

    La parte del programa que se encarga de volcar una de estas secuencias a la pantalla es ésta:

    ld hl,&c000
    ex af,af'
    ld (hl),a
    rlca
    ex af,af'
    ld de,&c001
    ld bc,16383
    ldir
    

    El registro A' contiene el próximo valor de la secuencia.

    Lo que he hecho entonces es aplicar el analizador lógico y programarlo para que comience la captura en cuanto se encuentre con un flanco negativo en la señal de escritura WE de cualquiera de las memorias dinámicas. Esto hará que lo primero que se vea sea la escritura que se hace en la instrucción LD (HL),A y poco después, un tren de escrituras periódico, causados por las sucesivas ejecuciones de LDIR.

    Atención al método que uso para rellenar la pantalla. Seguro que todos lo conoceis: es simplemente un LDIR que escribe en la siguient posición de memoria lo que hay en la actual. Lo recalco aquí porque el hecho de usar este método es lo que me ha permitido averiguar lo que he averiguado...

    Para empezar, un vistazo general. Esto es lo que se ve cuando el ACID está puesto.

    La última señal es WE (Write Enable) y se ve perfectamente como hay una primera escritura, y después una serie de ellas con la misma duración una de la siguiente. La primera escritura es la de LD (HL),A y las siguientes, las de LDIR.

    Vemos también que SIN cambia de forma aparentemente errática. No se observa ningún patrón. CCLR no se muestra aquí, pero está permanentemente en estado alto.

    El analizador me permite colocar una serie de cursores, que no son más que marcas temporales, para las cuales puedo visualizar el valor de todas las señales en el instante en que esa marca se produjo. Los cursores son A,B,C,D y E.

    A está colocado en el momento en que el Z80 escribe el dato de LD (HL),A . El valor de RAMDATA es el valor que la memoria "ve", y es el que realmente se va a escribir en ella.

    B está colocado en el momento en que el Z80 lee un valor de memoria dentro de la primera iteración de la instrucción LDIR. Recordad que LDIR puede descomponerse internamente en las siguientes instrucciones:

    ld reg,(hl)
    ld (de),reg
    inc hl
    inc de
    dec bc
    si bc<>0 vuelve a empezar

    Donde "reg" es un registro interno. No se usa el acumulador en esta instrucción.

    Pues bien. El cursor B está situado en el momento en que se hace LD reg,(HL) por primera vez.

    C está colocado en el momento en que se ejecuta LD (DE),reg en la primera iteración de LDIR.

    D y E están situados en la lectura y escritura posterior, de la segunda iteración de LDIR.

    Recalco que el valor RAMDATA es el valor que la memoria "ve" desde el Z80 (mejor dicho, desde el gate array) cuando se escribe algo en ella. En una operación de lectura, es el valor que la RAM entrega al gate array para que éste se lo dé al Z80. Esto es importante recordarlo para lo que viene después.

    Seguimos: otro vistazo general, esta vez sin el ACID puesto.

    SIN no se muestra aquí porque tiene un comportamiento errático, que bien pudiera deberse a ruido, con transiciones más rápidas que el propio reloj. Sin embargo CCLR tiene un comportamiento mucho más predecible, como se puede observar.

    Ahora vamos a ver qué pasa con la memoria, paso a paso.

    1. Ejecución de LD (HL),A . A contiene un valor de la secuencia 01h, 02h, 04h, 08h, 10h, 20h, 40h, 80h, FEh, FDh, FBh, F7h, EFh, DFh, BFh, 7Fh

    Con ACID:

    Sin ACID:

    Aparentemente, en ambos casos la escritura se produce con normalidad. El ciclo de escritura RAS-WE-CAS es correcto, y el dato que la memoria ve (FDh) es el esperado.

    2. Entramos en LDIR y se ejecuta la primera iteración de esta macroinstrucción: se lee de memoria un valor desde la dirección contenida en el registro HL (que es la misma dirección donde acabamos de escribir FDh), y justo después se escribe ese valor en la dirección dada por DE. Estos dos instantes están marcados por los cursores B y C.

    Con ACID:

    Sin ACID:

    Aquí comienzan las diferencias. El analizador muestra (ver cursor B) que en ambos casos se ha accedido a la dirección marcada por HL y se ha leido un valor correcto (FDh).

    Pero a la hora de escribir (cursor C), con el ACID se ve cómo se escribe el valor FDh (que se acaba de leer en B), y sin embargo, sin el ACID, el valor que se escribe es uno muy distinto (B0h).

    ¿Cómo es posible esto? Hemos visto (cursor A) que el Z80 escribe correctamente valores en memoria, y que esos valores son leidos de la misma (cursor B). Entonces, ¿de dónde viene el valor que se escribe en el cursor C?

    Muy sencillo: recordad que lo que estamos viendo en el analizador son los valores que la memoria RAM "ve". Nadie ha asegurado que esos mismos valores son los que el Z80 ve.

    Y ahí está todo el quid de la cuestión: EL GATE ARRAY ESTA ENGAÑANDO AL Z80. EN LECTURA, NO LE DEVUELVE LOS VALORES REALES DE LA MEMORIA.

    El Z80 no recibe en B el valor FDh que la memoria le entrega, sino que recibe "de parte del gate array", el valor B0h (parece ser que siempre es este mismo valor). De tal forma, que obediente, cuando va a escribir en memoria el valor que ha "leido", escribe B0h (cursor C).

    3. Segunda iteración de LDIR.

    Dado que cada iteración de LDIR lee el valor que se escribió en la anterior, y lo escribe en la actual, en esta segunda iteración, lo que se lee de memoria será el valor B0h que la CPU escribió en el cursor C. Esto es lo que pasa en el cursor D. En el cursor E, al igual que en C, el Z80 escribe el valor que ha leido, que no es otro que B0h. Aquí muestro solamente el cronograma con el ACID quitado.

    Del análisis de estos cronogramas se deduce el siguiente comportamiento cuando no está el ACID puesto:

    • Que las escrituras del procesador a memoria son correctas. Tanto la dirección como el dato se guardan correctamente en memoria. La dirección es correcta porque el patrón que aparece en pantalla es consistente con el dato guardado (un dato correcto, perteneciente a la secuencia mencionada, en C000h y el resto de direcciones conteniendo B0h). No he puesto foto de la pantalla porque no he conseguido una buena donde se aprecie bien que la salida es correcta.
    • Que el Gate Array lee la memoria sin problemas y muestra resultados coherentes en pantalla.
    • Que en escritura, la memoria vuelca un dato correcto al Gate Array. Sin embargo, éste no devuelve ese resultado al Z80 sino que le entrega un valor fijo que, al menos en mi placa, es B0h.
    • Del comportamiento del DMA no se puede asegurar nada, pero dado que el DMA es sin el concurso de la CPU, y dado que el gate array lee la memoria correctamente, es lógico suponer que el DMA no se ve afectado por el ACID.

    Ahora entramos en el terreno de las hipótesis:

    Suponiendo que el ACID realmente sólo afecta a las lecturas que el Z80 hace a memoria en la manera en que he comentado, la forma de anular este comportamiento consiste en añadir dos transceivers al bus de datos del Z80. La dirección de los transceivers estará gobernada por las señales RD y WR.

    Uno de los transceivers conectara el bus de datos del Z80 directamente con el bus de datos de la RAM. Este transceiver sólo se habilitará cuando se detecte una lectura a memoria RAM.

    El otro transceiver conectará el bus de datos del Z80 con el bus de datos "oriignal" que va a los pines de datos del Z80. Se habilitará en todos los demás casos en los que no se habilite el otro transceiver, es decir, cuando se escriba a memoria, o cuando se haga cualquier operación de E/S.

    Aún así, faltaría solucionar un problema: ¿cómo detectamos cuando se está leyendo realmente de memoria RAM? La cosa no es trivial, ya que una operación de lectura de memoria debe tener en cuenta:

    • Que leer una posición de memoria en concreto puede suponer leer ROM y no RAM. Esto dependerá de cómo esté mapeado el sistema en determinado momento, lo que significa monitorizar los puertos de E/S donde se guarda esta información.
    • Que leer una posición de memoria puede suponer acceder en realidad a uno de los puertos del gate array que está mapeado en memoria (direcciones 6000h en adelante si mal no recuerdo).

    El hecho de que el ACID use A0-A7 como entrada me resulta curioso... sobre todo porque acabo de caer en la cuenta de que en el Z80, A0-A7 tienen un uso muy concreto dentro del ciclo M1: es la dirección de fila de refresco de la RAM. En otras palabras, durante el ciclo de refresco, A0-A7 se comporta como un contador de 0 a 127 y vuelta a empezar. ¿Tendrá algo que ver en el ACID? Hay una forma de comprobarlo: A7 siempre tiene el valor 0 durante el ciclo de refresco. Si al retirar A7 del ACID, éste sigue funcionando, entonces quizás tenga que ver.

    Mientras tanto, he pensado en un "workaround", un parche feo, tosco y nada elegante, pero que podría servir. La idea se basa en suponer que el ACID unicamente "estropea" los accesos a lectura desde el Z80 a la memoria. También se basa en que el cronograma de dichos accesos sigue un patrón fácilmente reconocible, y es éste:

    El ciclo comienza en el punto donde está el cursor F, es decir, cuando baja MREQ, y termina cuando sube esta misma señal. En todos los accesos de lectura a memoria he observado que se sigue este patrón temporal:

    • Primero baja MREQ. RAS, CAS y WE están a nivel alto.
    • Aproximadamente 40ns después (cursor B) baja RAS. MREQ sigue a nivel bajo, y CAS y WE siguen a nivel alto.
    • Unos 120ns después baja CAS. MREQ y RAS siguen a nivel bajo y WE sigue a nivel alto.
    • Mucho más tarde, sube MREQ. El estado de RAS y CAS no es importante. WE ha estado todo este tiempo a nivel alto.

    Si este patrón es consistente y único, es posible diseñar una máquina de estados cuyas entradas sean MREQ, RAS, CAS, y WE, y cuya salida sea una señal de ENABLE para un transceiver (que supondré activa a nivel bajo). El diagrama de estados de este chip que llamaremos chip ALMAX podría ser éste:

    La salida de este chip, como digo, estaría ligada a un transceiver que uniría D0-D7 en la RAM con D0-D7 en el Z80. Los mismos pines D0-D7 del Z80 estarían unidos al bus de datos original desacoplándolo mediante resistencias de 220 ohmios. Cuando el transceiver está en alta impedancia, el Z80 ve los datos D0-D7 que le llegan del bus de datos original. Cuando el transceiver está activo, sus líneas de datos, unidas directamente al Z80, tienen más prioridad que las del bus original, y será entonces el dato que llegue de RAM al transceiver el que el Z80 vea.

    Esta máquina de estados deberá tener un reloj rápido. El intervalo más corto entre transiciones es el que se produce desde que baja MREQ hasta que baja RAS, y eso son 40ns, con lo que, según el teorema del muestreo, el chip debería realizar transiciones en la máquina de estados cada 20ns. Eso significa usar un reloj de 50MHz.

    Una GAL16V8 sería más que suficiente para implementar esta máquina, y además permite frecuencias de reloj de más de 100MHz.

    Dos días más tarde, constataba los malos augurios sobre el comportamiento del ACID. El "ALMAX" no serviría de gran cosa...

    A partir de la información de Arnoldemu, he escrito un nuevo programa que escribe y lee de ciertas posiciones de memoria, tanto con el ASIC mapeado como sin mapear. Este es un fragmento del mismo, concretamente en el que se testean algunas posiciones dentro del rango 4000-7FFF con el ASIC mapeado, esto es, los valores leidos y escritos no lo son de la memoria, sino que están dentro de propio ASIC.

    Como las sondas para el bus de datos las tengo en D0-D7 pero de la RAM dinámica, para poder ver qué estoy leyendo del ASIC lo que hago es escribir una "copia" del valor leído a una posición de memoria fuera del espacio mapeado por el ASIC. Esa escritura provoca un flanco negativo en WE, y esa condición es la que me vale para poder ver, ya en el bus de datos de la memoria, el valor que acabo de leer del ASIC en una instrucción anterior. Por eso hay instrucciones del tipo LD (DE),A que son las que me permiten ver los valores interesantes.

    Los seis cursores del analizador están apuntando precisamente a las seis escrituras que se hacen en el siguiente fragmento de código:

    ;page in asic ram between &4000-&7ffff
                    ld bc,&7fb8
                    out (c),c
    
                    ld de,&c000
                    ld a,&55
                    ld (de),a    ;First write, to trigger the logic analyzer. 55h is written to RAM
    
                    ld hl,&4000
                    ld (hl),&cc
                    ld a,(hl)    ;A read here return only the low nibble
                    ld (de),a    ;This has to write 0Ch into RAM.
    
                    ld hl,&6000
                    ld (hl),&77
                    ld a,(hl)    ;A read here returns the whole data
                    ld (de),a    ;This has to write 77h into RAM.
    
                    ld hl,&6001
                    ld (hl),&82
                    ld a,(hl)    ;This read returns 02h (low nibble? or data & 03h?)
                    ld (de),a    ;This has to write 02h into RAM.
                    inc a
                    ld (hl),a    ;03h is written, so...
                    ld a,(hl)    ;... a read will return FFh, as Arnoldemu doc states.
                    ld (de),a    ;This has to write FFh into RAM.
    
                    ld hl,&6800
                    ld (hl),&aa
                    ld a,(hl)    ;A read here returns invalid data (7Eh)
                    ld (de),a    ;This has to write 7Eh into RAM.
    

    Con el ACID puesto, obtenemos los siguientes valores en los 6 cursores (en este caso no pondré el cronograma completo, porque no hace falta ver el ciclo de escritura completo, sólo nos interesa qué dato se escribe en RAM):

    Sin el ACID sin embargo, obtenemos lo siguiente :(

    Sólo la primera escritura coincide, que es la que hace la CPU con un dato fijo, a memoria RAM. Esto ya vimos en el test anterior que funcionaba incluso sin el ACID. Pero el resto de accesos a memoria, que están interactuando con el ASIC (sprites, DMA, etc.) no devuelven en lectura el valor esperado, sino que devuelven lo que Arnoldemu llama "valor inválido", es decir, como si en esa zona no hubiera nada mapeado por el ASIC. Esto puede ser porque:

    • O bien la escritura está siendo interceptada y el valor escrito no llega correctamente al ASIC
    • O bien la lectura está siendo interceptada y el valor esperado no llega a la CPU, como pasa con la memoria RAM.

    Me inclino por este último comportamiento, aunque como en realidad no he hecho "nada" visible con el ASIC (como por ejemplo, poner un sprite en pantalla) no puedo asegurar que sólo pase esto último. ¿Alguien tiene un código para poner un sprite hardware en pantalla?

    Más tarde conseguí escribir un programa que mueve un sprite por la pantalla usando el hardware del CPC+ y confirmé este punto.

    Incluso aunque sólo hubiera problemas para leer y no para escribir, cualquier programa preparado para CPC464+ que necesite leer datos de la zona mapeada por el ASIC, fallará si no está el ACID puesto.

    Conclusión: LA NO PRESENCIA DEL CHIP ACID HACE QUE EL ASIC INTERCEPTE CUALQUIER ACCESO DE LECTURA A RAM, YA SEA RAM DE VERDAD, O REGISTROS DEL ASIC MAPEADOS A RAM, Y ENTREGUE DATOS FALSOS A LA CPU.

    Y esto es lo malo: al contrario de la memoria RAM, que podíamos hacerle un bypass al ASIC leyendo directamente de ella, no hay forma de acceder a la memoria interna del ASIC si el propio ASIC no nos deja.

    Con lo que está claro que para obtener un CPC464+ plenamente funcional, no vale la solución que propuse. Hay que desentrañar el comportamiento del ACID y clonarlo. Con el ALMAX como mucho se podrá asegurar que el ordenador funcione en modo compatible con el CPC. Se podrían usar las características del CPC+ (tendría que comprobarlo si alguien me pasa un código que haga algo que sólo puede hacerse con un CPC+) pero con la condición de que el código no necesite leer de la zona mapeada por el ASIC.

     

    Y hasta aquí llegué. El resto de las conversaciones en el hilo se centraron en buscar a "alguien" que hubiera trabajado en el diseño del ASIC y del ACID, y nos pudiera dar información relevante sobre cómo funciona el mismo. No tuvimos suerte. Mientras tanto, No$Cash consiguió lo imposible...

     

    Intercambié algunos correos con No$Cash ya que él había destripado el algoritmo pero no tenía claras algunas de las señales, ya que no uso un CPC+ conectado al ACID. Le pasé algunos cronogramas pero no era lo que él estaba buscando. Le prometí que en cuanto pudiera volvería a pinchar el analizador lógico en el CPC+. La cosa se demoró casi un año (en medio de todo esto me casé, con lo que todo ello conllevó.

    Y así llegamos al mes de Marzo de este año 2011. Con algo de experiencia en Verilog (un lenguaje HDL de amplia difusión en Estados Unidos) recuperé la placa semidesnuda del CPC+, el analizador lógico, y el cartucho del Brnin' Rubber con la intención de obtener gráficas detalladas del comportamiento en los flancos de las señales SIN, CLK y CCLR, y así poder responder a las preguntas de No$Cash. Le mandé la información y le pregunté que cómo es que no había noticias de una implementación hardware del ACID. No me llegó a responder.

    Buscando en Internet, topé con una galería de fotos de una retroparty donde se mostraba a dos personas (Octoate y Nilquader) con un CPC+ que al parecer habían conseguido clonar el ACID. Extrañamente no había rastro de código HDL por ninguna parte.

    Uno de los hackers, Octoate, ha publicado recientemente toda la información que consiguieron en Octubre del 2010, cuando clonaron el ACID. ¡¡¡BIEN por ellos!!! :)

    Ya que no había nada publicado, quise probar suerte. Lamentablemente, el único cartucho de que disponía estaba muy perjudicado con tanto soldar y desoldar. Para colmo, enredar con las sondas del analizador lógico hacía el sistema aún más inestable. Resolví crear un cartucho de diagnóstico, que me permitiera llevar a cabo comodamente cualquier experimento que necesitara.

    La tira de pines más larga contiene todas las señales del ACID, y me permite mirar la evolución de las mismas en el analizador lógico. A su derecha, tres pines me permiten elegir con un jumper si la CPLD se va a alimentar con 5 voltios (familia XC9000 de Xilinx) o con 3.3V (familia XC9500XL). En el costado izquierdo, el JTAG para programar la CPLD, y en la parte de abajo, otros tres pines que me permiten, usando otro jumper, elegir si la señal SIN proviene del ACID "de verdad" o de la CPLD. Completa el cartucho un zócalo grande para la EPROM, uno pequeño para el ACID, y otro PLCC44 para la CPLD.

    Con él sí que llegué a clonar el chip ACID, usando como base el algoritmo de No$Cash. La descripción en Verilog a la que llegué es la siguiente:

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: Dept.  Architecture and Computer Technology. University of Seville.
    // Engineer:       Miguel Angel Rodriguez Jodar.
    // 
    // Create Date:    13:08:05 04/01/2011 
    // Design Name: 
    // Module Name:    acid 
    // Project Name: 
    // Target Devices: XC9536 / XC9536XL y superiores (tolerantes a 5V)
    // Tool versions:  ISE Webpack 12.4
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    
    module genxorvals (
    	input [7:0] a,
    	output [16:0] cmpval,
    	output [16:0] xorval
    	);
    	//CmpVal=13596h == 1 0011 0101 1001 0110
    	assign cmpval[0] = a[5];
    	assign cmpval[1] = ~a[5];
    	assign cmpval[2] = ~a[0];
    	assign cmpval[3] = a[0];
    	
    	assign cmpval[4] = ~a[3];
    	assign cmpval[5] = a[3];
    	assign cmpval[6] = a[2];
    	assign cmpval[7] = ~a[2];
    	
    	assign cmpval[8] = 1'b1;
    	assign cmpval[9] = a[6];
    	assign cmpval[10] = ~a[6];
    	assign cmpval[11] = a[7];
    	
    	assign cmpval[12] = ~a[7];
    	assign cmpval[13] = ~a[1];
    	assign cmpval[14] = a[1];
    	assign cmpval[15] = a[4];
    	
    	assign cmpval[16] = ~a[4];
    	
    	//XorVal=0c820h == 0 1100 1000 0010 0000
    	assign xorval[1:0] = 2'b00;
    	assign xorval[2] =  a[0];
    	assign xorval[4:3] = 2'b00;
    	assign xorval[5] = ~a[3];
    	assign xorval[6] = 1'b0;
    	assign xorval[7] = a[2];
    	assign xorval[10:8] = 3'b000;
    	assign xorval[11] = ~a[7];
    	
    	assign xorval[12] = 1'b0;
    	assign xorval[13] = a[1];
    	assign xorval[14] = ~a[1];
    	assign xorval[15] = ~a[4];
    	
    	assign xorval[16] = 1'b0;
    endmodule
    
    module acid (
    	input clk,
    	input [7:0] a,
    	input ce,
    	input cclr,
    	output sin
    	);
    	
    	reg [16:0] sreg = 17'h1FFFF;
    	wire [16:0] xorval;
    	wire [16:0] cmpval;
    	wire [16:0] sregxored;
    	wire newbit;
    	
    	genxorvals generador (a, cmpval, xorval);		
    	
    	assign newbit = ^{sreg[0],sreg[9],sreg[12],sreg[16]};
    	assign sregxored = sreg ^ xorval;
    	assign newbitxored = ^{sregxored[0],sregxored[9],sregxored[12],sregxored[16]};
    	assign sin = sreg[0];
    	
    	always @(negedge clk)
    		if (!cclr)
    			sreg <= 17'h1FFFF;
    		else if (!ce && ((sreg | 17'h00100) == cmpval))
    			sreg <= {newbitxored,sregxored[16:1]};
    		else
    			sreg <= {newbit,sreg[16:1]};
    endmodule
    

    Este es el resultado: el cartucho de diagnóstico ejecutando Pinball Magic, usando a una XC9572 (el código se puede sintetizar en una XC9536) como chip ACID. El propio chip ACID es testigo de todo, deconectado y puesto lejos de la placa :D

    Algunos datos técnicos de la implementación. La herramienta ISE de Xilinx da los siguientes reportes:

    Fitter Report

     Design Name  acid
     Fitting Status  Successful
     Software Version  M.81d
     Device Used  XC9536-15-PC44
     Date   4-14-2011, 11:20PM

    RESOURCES SUMMARY
    Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
    23/36  (64%) 116/180  (65%) 17/36  (48%) 12/34  (36%) 64/72  (89%)

    PIN RESOURCES
    Signal Type Required Mapped
     Input  10  10
     Output  0  0
     Bidirectional  1  1
     GCK  1  1
     GTS  0  0
     GSR  0  0
    Pin Type Used Total
     I/O   9  29
     GCK/IO  1  3
     GTS/IO  2  2
     GSR/IO  0  1

    GLOBAL RESOURCES
     Signal mapped onto global clock net (GCK1)  /clk

    POWER DATA
     Macrocells in high performance mode (MCHP)  23
     Macrocells in low power mode (MCLP)  0
     Total macrocells used (MC)  23

    Si alguien se pregunta si podría construirse un clon del ACID usando chips discretos, pues... esto es lo que aparece en el informe de síntesis sobre los recursos utilizados, incluyendo qué tipo de puertas lógicas se han usado y cuántas.

    =========================================================================
    *                            Final Report                               *
    =========================================================================
    Final Results
    RTL Top Level Output File Name     : acid.ngr
    Top Level Output File Name         : acid
    Output Format                      : NGC
    Optimization Goal                  : Speed
    Keep Hierarchy                     : Yes
    Target Technology                  : XC9500 CPLDs
    Macro Preserve                     : YES
    XOR Preserve                       : YES
    wysiwyg                            : NO
    
    Design Statistics
    # IOs                              : 12
    
    Cell Usage :
    # BELS                             : 150
    #      AND2                        : 29
    #      AND3                        : 1
    #      AND8                        : 2
    #      INV                         : 68
    #      OR2                         : 21
    #      OR3                         : 7
    #      XOR2                        : 22
    # FlipFlops/Latches                : 17
    #      FD                          : 17
    # IO Buffers                       : 12
    #      IBUF                        : 11
    #      OBUF                        : 1
    =========================================================================

    Otra de las herramientas que ofrece el ISE de Xilinx e suna vista del esquemático equivalente del chip. Puede verse por niveles. Así, el primer nivel, en que se muestra el chip completo como una caja negra, es éste:

    Parece tan enigmático como el chip original, ¿no? Haciendo doble clic en una esquina de la "caja negra", el ISE nos desvela la circuitería que ha sintetizado (clic en la imagen para verla a tamaño completo)...

 
Comments (3)
Cartucho flash
3 Domingo, 18 de Diciembre de 2011 15:35
Miguel Angel
Sí. Con esta información ya se tiene todo lo necesario para poder diseñar y crear cartuchos flash para la GX4000.
Yo no sé si va esto
2 Martes, 06 de Diciembre de 2011 14:31
Luis
a ver no entiendo de electrónica a decir la verdad de casi nada, soy cocinero jajjaa pero si que te puedo decir que soy amante del amstrad gx 4000, por la nostalgia que nos dio de pequeños en casa, hará cosa de un año me hice con dos modelos pero por desgracia solo tengo 3 juegos y me pregunta es si sería posible hacerse con un cartucho flash como los de la game boy o algo similar. Un saludo y muchas gracias.
buenos días.
1 Martes, 06 de Diciembre de 2011 14:24
Luis
a ver no entiendo de electrónica a decir la verdad de casi nada, soy cocinero jajjaa pero si que te puedo decir que soy amante del amstrad gx 4000, por la nostalgia que nos dio de pequeños en casa, hará cosa de un año me hice con dos modelos pero por desgracia solo tengo 3 juegos y me pregunta es si sería posible hacerse con un cartucho flash como los de la game boy o algo similar. Un saludo y muchas gracias.

Add your comment

Your name:
Your email:
Título:
Comment:
  The word for verification. Lowercase letters only with no spaces.
Word verification:
ZX Projects, Powered by Joomla! and designed by SiteGround web hosting