Generador de ruido del YM2151

El ruido que oímos en los juegos, normalmente usado para simular explosiones, golpes, etc. es generado de forma aleatoria en el interior del chip. ¿Aleatoria de verdad? Bueno, aunque los procesadores modernos sí suelen llevar un verdadero generador de números aleatorios basado en propiedades físicas de los circuitos, en el pasado y en muchas aplicaciones modernas se usa un tipo de circuito llamado registro de desplazamiento con realimentación lineal, LFSR por sus siglas en inglés (wikipedia).

En el código fuente del emulador del MAME (ym2151.c) encontramos la siguiente tabla para emular el ruido:


/*
Noise LFO waveform.
Here are just 256 samples out of much longer data.
It does NOT repeat every 256 samples on real chip and I wasnt able to find
the point where it repeats (even in strings as long as 131072 samples).
I only put it here because its better than nothing and perhaps
someone might be able to figure out the real algorithm.
Note that (due to the way the LFO output is calculated) it is quite
possible that two values: 0x80 and 0x00 might be wrong in this table.
To be exact:
some 0x80 could be 0x81 as well as some 0x00 could be 0x01.
*/
static UINT8 lfo_noise_waveform[256] = {
0xFF,0xEE,0xD3,0x80,0x58,0xDA,0x7F,0x94,0x9E,0xE3,0xFA,0x00,0x4D,0xFA,0xFF,0x6A,
0x7A,0xDE,0x49,0xF6,0x00,0x33,0xBB,0x63,0x91,0x60,0x51,0xFF,0x00,0xD8,0x7F,0xDE,
0xDC,0x73,0x21,0x85,0xB2,0x9C,0x5D,0x24,0xCD,0x91,0x9E,0x76,0x7F,0x20,0xFB,0xF3,
0x00,0xA6,0x3E,0x42,0x27,0x69,0xAE,0x33,0x45,0x44,0x11,0x41,0x72,0x73,0xDF,0xA2,
0x32,0xBD,0x7E,0xA8,0x13,0xEB,0xD3,0x15,0xDD,0xFB,0xC9,0x9D,0x61,0x2F,0xBE,0x9D,
0x23,0x65,0x51,0x6A,0x84,0xF9,0xC9,0xD7,0x23,0xBF,0x65,0x19,0xDC,0x03,0xF3,0x24,
0x33,0xB6,0x1E,0x57,0x5C,0xAC,0x25,0x89,0x4D,0xC5,0x9C,0x99,0x15,0x07,0xCF,0xBA,
0xC5,0x9B,0x15,0x4D,0x8D,0x2A,0x1E,0x1F,0xEA,0x2B,0x2F,0x64,0xA9,0x50,0x3D,0xAB,
0x50,0x77,0xE9,0xC0,0xAC,0x6D,0x3F,0xCA,0xCF,0x71,0x7D,0x80,0xA6,0xFD,0xFF,0xB5,
0xBD,0x6F,0x24,0x7B,0x00,0x99,0x5D,0xB1,0x48,0xB0,0x28,0x7F,0x80,0xEC,0xBF,0x6F,
0x6E,0x39,0x90,0x42,0xD9,0x4E,0x2E,0x12,0x66,0xC8,0xCF,0x3B,0x3F,0x10,0x7D,0x79,
0x00,0xD3,0x1F,0x21,0x93,0x34,0xD7,0x19,0x22,0xA2,0x08,0x20,0xB9,0xB9,0xEF,0x51,
0x99,0xDE,0xBF,0xD4,0x09,0x75,0xE9,0x8A,0xEE,0xFD,0xE4,0x4E,0x30,0x17,0xDF,0xCE,
0x11,0xB2,0x28,0x35,0xC2,0x7C,0x64,0xEB,0x91,0x5F,0x32,0x0C,0x6E,0x00,0xF9,0x92,
0x19,0xDB,0x8F,0xAB,0xAE,0xD6,0x12,0xC4,0x26,0x62,0xCE,0xCC,0x0A,0x03,0xE7,0xDD,
0xE2,0x4D,0x8A,0xA6,0x46,0x95,0x0F,0x8F,0xF5,0x15,0x97,0x32,0xD4,0x28,0x1E,0x55
};

Jarek Burczynski, el autor del emulador del YM2151 usado en MAME y casi en cualquier otro sitio, habla sobre cómo estas medidas de la salida del YM2151 no son periódicas, incluso después de más de cien mil muestras. También dice que algunos valores podrían estar mal debido a la forma de extraer los datos y su relación con el oscilador de baja frecuencia del chip (el LFO).

Asumiendo que estos valores debían de provenir de un LFSR y que lo más lógico era tener un LFSR por cada bit, apliqué el algoritmo de Berlekamp-Massey (wikipedia) para obtener ese supuesto registro de desplazamiento. Y este fue el resultado:

 Para todos los bits, salvo el 0 y el 4, de la secuencia de Jarek B. existe un LFSR de orden 19 y realimentación en las etapas 0, 1, 14, 15, 17 y 18.

Así que los bits que no se extrajeron bien en algunos casos fueron el 0 y el 4 y no el 0 y el 8 como pensaba Jarek. Fijaos que es la misma secuencia pseudoaleatoria para todos los bits de la palabra de ruido pero como cada LFSR está iniciado en un punto distinto de la secuencia, no van a coincidir nunca. La secuencia completa de cada bit tiene una longitud de 2^18, mucho mayor que los cien mil valores que Jarek tomó del chip. Además para ver una repetición tiene que coincidir que todas las secuencias se estén repitiendo. No voy a hacer el cálculo, pero el periodo de la secuencia es mucho mayor que 2^18.

Con estos datos ya es posible usar el mismo circuito que el original para producir el mismo ruido. Esto se puede aplicar a corregir el fichero ym2151.c de MAME o a seguir escribiendo el clón en verilog, que es lo que me ocupa a mí.

En la próxima entrada escribiré los valores con los que hay que iniciar cada LFSR para obtener la secuencia de ruido.

Primera nota musical con el YM2151

Después de prepararme para controlar un YM2151 a través de MAME (explicación). Aquí va mi primera nota, no sin esfuerzo. Para ello escribo los siguientes valores en los registros:

Registro Nombre Valor Explicación
Del $60 al $7F Total Level $00 volumen
$20 Connection $C7 $C = Salida por izquierda y derecha
$7 = conexión tipo 7
 $28 Key Code $4A $4 = octava
$A = nota (la)
 $40 Phase Multiplier 1
 $C0 Detune 2 0
 $38 Phase Modulation Sensitivity 0
Del $80 al $83 Attack Rate $10 Comienzo de la envolvente
Del $A0 al $A3 Decay 1 Rate $10 Caída de la envolvente
Del $C0 al $C3 Decay 2 Rate $10 Caída de la envolvente
Del $E0 al $E3 Release Rate $8 Caída de la envolvente
 $78 Key On $78 Comienza la envolvente para todos los operadores del canal 0

Sólo con esto ya se consigue sonido… una vez. Para repetirlo hay que volver a escribir a YM_KEYON ($78), esta vez con un $00 y luego volver a escribir un $78 para que empiece el sonido de nuevo. La repetición la hago en el código usando los temporizadores del YM2151 para esperar aproximadamente medio segundo.

A continuación está el código completo en ensamblador del 6809. Este código lo ensamblo y corro con el MAME.


; YM Registers
YM_KEYON EQU $08
YM_TIMERB EQU $12
YM_TIMER_CTRL EQU $14
YM_CT EQU $1B
YM_CONNECTION EQU $20
YM_KEY_CODE EQU $28
YM_KEY_FRACTION EQU $30
YM_PMS EQU $38
YM_PHASE_MUL EQU $40
YM_DETUNE2 EQU $C0
YM_ATTACK_RATE EQU $80
YM_DECAY1_RATE EQU $A0
YM_DECAY2_RATE EQU $C0
YM_RELEASE_RATE EQU $E0
; YM LOCATION IN THE MEMORY MAP
YM_ADR EQU $2000
YM_DATA EQU $2001
ORG $8000
RESET LDD #$6800
TFR D,S
; SET TOTAL LEVEL TO 00 FOR ALL CHANNELS
LDA #$60
STA $6000
TL_NXT: LDA $6000
CMPA #$80
BEQ PLAY
LDB #$00
JSR WRITE_YM
INC $6000
BRA TL_NXT
PLAY JSR WAIT
LDB #$C7
LDA #YM_CONNECTION
JSR WRITE_YM
LDB #$4A ; A 440Hz
LDA #YM_KEY_CODE
JSR WRITE_YM
LDB #1
LDA #YM_PHASE_MUL
JSR WRITE_YM
CLRB
LDA #YM_DETUNE2
JSR WRITE_YM
CLRB
LDA #YM_PMS
JSR WRITE_YM
; Envelope
CLRA
EG58 STA $6000
LDA #YM_ATTACK_RATE
ADDA $6000
LDB #$10
JSR WRITE_YM
LDA #YM_DECAY1_RATE
ADDA $6000
LDB #$10
JSR WRITE_YM
LDA #YM_DECAY2_RATE
ADDA $6000
LDB #$10
JSR WRITE_YM
LDA #YM_RELEASE_RATE
ADDA $6000
LDB #$8
JSR WRITE_YM
LDA $6000
INCA
CMPA #4
BNE EG58
KEYON:
LDB #$78
LDA #YM_KEYON
JSR WRITE_YM
; Wait for 0.5s before starting again
LDA #7
STA $6000
L59 LDA #YM_TIMERB
LDB 0
JSR WRITE_YM
LDA #YM_TIMER_CTRL
LDB #$2A
JSR WRITE_YM
L60 LDA YM_DATA
ANDA #2
BEQ L60
LDA #YM_TIMER_CTRL
LDB #$30 ; Reset flags
JSR WRITE_YM
DEC $6000
BNE L59
; key off
LDB #$00
LDA #YM_KEYON
JSR WRITE_YM
LBRA PLAY ; play it again, Sam
WAIT LDA #$FF
LOOP DECA
BNE LOOP
RTS
WRITE_YM:
JSR WAIT_YM
STA YM_ADR
JSR WAIT_YM
STB YM_DATA
RTS
WAIT_YM:
PSHS A ;
LDA YM_DATA
ANDA #$80
BNE WAIT_YM
PULS A ;
RTS
IRQ_SER RTI
TopMem EQU $FFF8
FILL $FF,TopMem-*
FDB IRQ_SER ; $FFF8
FDB $FFFF, $FFFF
FDB RESET

Sacar sonido del YM2151 en MAME

Después de más de un año de parada retomo la labor de clonar el YM2151 en FPGA. Los chips FM de Yamaha siguen sin estar disponibles en Verilog/VHDL y son una parte crucial si algún día queremos ver equipos de 16 bits (Megadrive, X68000, CPS1, SEGA System 16, etc.) recreados en FPGA y librarnos de los problemas de temporización e inexactitudes de la emulación.

El problema del YM2151 es su pésima documentación. No sólo porque el documento que circula escaneado por internet esté borroso y contenga partes ilegibles, sino porque en sí era un mal documento. El mapa de registros no está bien explicado y no hay ni un puñetero ejemplo de cómo generar sonidos. Esto en Japón, con los ingenieros de Yamaha al lado no sería gran problema. O quizá en su momento había herramientas de generación de sonidos que facilitaban la labor de programación de estos chips. Pero hoy en día, con sólo la documentación es imposible aclararse.

Jarek Burczynski además de lograr escribir su propio apellido, escribió el emulador de YM2151 usado en MAME. Cuenta en los créditos (en el ym2151.h) que contó con la colaboración de Shigeharu Isoda, un ingeniero de sonido de SEGA que conocía bien el chip. Shigeharu le pasó la documentación en japonés (ficheros que no han visto la luz de internet, por cierto) y sobre todo contestó a muchas preguntas. Algo fundamental si esa documentación en japonés es tan pobre como la que existe en inglés. También contó con la ayuda de otros con acceso a un chip real, quienes le tomaron medidas.

Pues bien, dada la escasez de documentación útil del YM2151, el emulador de Jarek B. va a ser una pieza fundamental para desentrañar este chip. Primero trataré de hacer funcionar las cosas en el emulador y luego de replicarlas en el chip real. Compararé los resultados y, por supuesto, trataré de replicar el chip real en Verilog, no el emulador.

Podría coger los ficheros ym2151.c, ym2151.h de MAME, adaptarlos a mi propio programa en C y explorarlos así. Sin embargo, prefiero un mecanismo más fiel a la época. He tomado como referencia el juego Gryzor. De él, la ROM 633e01.12a contiene el código del procesador 6809 que dirigía el sonido. El procesador principal escribía el código del sonido en un registro al que tenía acceso también este coprocesador. Según el código, este 6809 mandaba instrucciones al YM2151 para reproducir la melodía o el efecto sonoro adecuado.

MAME permite ejecutar un juego con ROMs alternativas. Lo que he hecho es ensamblar mi propia versión de la ROM de sonido de Gryzor, donde ejecuto mi propio código, y correrla con MAME. De esta forma puedo escuchar la salida de sonido rápidamente. MAME advierte de que la ROM no contiene lo esperado pero reproduce el juego bien.

En la siguiente entrada explicaré cómo conseguí el primer sonido del YM2151 usando este método.

Sistemas retro en FPGA ¿Por qué?

Con todo el entusiasmo generado por el ZX Uno, un proyecto español originado en el foro Zona de pruebas, que recrea un ZX Spectrum (entre otros) en una pequeña placa con una FPGA, muchos se están preguntando… ¿qué es una FPGA? ¿Por qué es mejor que emular?

Hay un vídeo en youtube que trata de explicar esto en relación a otro gran proyecto abierto de recreación en FPGA, el Mist. Sin embargo, creo que la explicación no satisfará a muchos con ansias de entender mejor la ventaja. Así que me he animado a escribir esta entrada.

Supongo que sabes que un ordenador, clásico o no, se compone de básicamente de estos elementos:

  1. Procesador (CPU)
  2. Memoria
  3. Sistema de E/S (Entrada/Salida), incluye teclado, vídeo, sonido, cinta, disco…

En la imagen tenemos la placa del ZX Spectrum con estos componentes marcados. Hay memoria que contiene el sistema (el BASIC en un ordenador de 8 bits). A esta memoria la llamamos ROM, porque no puede borrarse. Hay otra memoria que se usa para guardar los datos con los que el procesador trabaja, que llamamos RAM. Por supuesto está la CPU y un chip bastante grande marcado como FERRANTI ULA que contiene los sistemas de entrada y salida. A su lado hay dos piezas metálicas que contienen cuarzo y se usan para generar el reloj del sistema. El número 4.4336 que puede leerse es la frecuencia en MHz para la que está diseñado ese cristal, usado en el circuito de vídeo.

ZXspectrum_mb2

Un emulador es un programa que recrea la función de todos estos elementos: uno a uno y dentro de las restricciones temporales, de sonido e imagen que plantea el sistema sobre el que corre el emulador. Sin embargo, en el equipo real estos chips funcionan todos a la vez. Es más, en su interior hay miles de componentes que también funcionan a la vez. El emulador trata de recrear lo que el programa original está haciendo. Si el programa original decía: coge dos números de memoria y súmalos. El emulador hará eso. Pero… la placa original no hacía exactamente eso.

Bueno, sumaba los dos números pero pasaban más cosas. Esos números eran transportados hasta el interior de la CPU. Almacenados en una memoria especial llamado “registro” del procesador y pasados a través de otra etapa dentro del procesador encargada de hacer sumas. Cada uno de estos elementos estaba fabricado con transistores y cada uno de estos transistores tenía su propia actividad que ocurría al ritmo marcado por el reloj: 3,5MHz en un Spectrum. Y mientras tanto el chip de vídeo seguía con su actividad, y el altavoz seguía funcionando y la cinta moviéndose… Todo ocurría a la vez (en paralelo).

En el emulador, todo ocurre en serie: paso a paso. El emulador simula qué hace la CPU durante un tiempo. Se para y mira qué hace el chip de vídeo. Se para y atiende al sonido. Vuelve la mirada hacia el teclado… Así que las cosas no pasan a la vez y normalmente esto no se nota. Los ratitos que el emulador dedica a cada tarea son lo suficientemente cortos como para que no se note… Casi nunca. Cuando un programa necesita, por diseño o por casualidad, una relación de tiempo más precisa entre los componentes pasamos a necesitar equipos mucho más rápidos para poder emular el sistema. Podéis leer aquí el caso de cómo emular una SNES puede llevarse por delante un procesador de 3GHz. Y aun así, será inexacto.

¿Qué es una FPGA?

Una FPGA es un chip en cuyo interior hay muchos componentes electrónicos cuyas conexiones son libres. Estos componentes son desde muy pequeñitos como flip flops (memorias de un solo bit) hasta memorias más grandes o incluso CPUs modernas (modernas sí, pero pequeñas). La mayoría de los componentes de la FPGA son bloques elementales de la electrónica digital. Son piezas muy pequeñas (puertas AND, OR, flip flops, sumadores, algún divisor) que el diseñador electrónico necesita unir para crear circuitos.

Dentro de los chips del ordenador de la imagen también se encuentran estas mismas piezas. La diferencia clave está en que en los chips del ordenador original esas piezas están conectadas con un metal que no se puede cambiar una vez fabricado. Mientras que en la FPGA la conexión es variable. Y aquí viene lo fascinante. Con la FPGA adecuada, es posible reconstruir el circuito electrónico original y por lo tanto tener el mismo sistema, solo que en un solo chip. Así que una FPGA no imita lo que hacía el sistema original. Una FPGA es como el sistema original. Por supuesto, en la recreación del circuito se pueden cometer errores o puede faltar información y ahí surgirán pequeñas diferencias. Pero el potencial de llegar a ser exactamente igual al original con precisión superior al microsegundo está ahí.

¿Cómo se programa una FPGA?

El término programar es confuso. Programar una FPGA realmente se refiere a transferir la información de las conexiones desde el ordenador, donde se ha desarrollado el sistema, hasta el chip de la FPGA. Una vez programada, la FPGA contiene las conexiones que el ingeniero quiere.

¿Cómo decide el ingeniero esas conexiones? Dado que para cada modelo de FPGA hay unos bloques elementales en su interior, el ingeniero puede decidir directamente qué bloque usar y a qué bloque conectarlo. Como quien une los puntos de un dibujo. Sin embargo, este enfoque no es el más práctico. El ingeniero prefiere escribir un código parecido al de un programa de ordenador. Este código describe lo que el circuito tiene que hacer. Aunque parezca un programa, no es realmente un programa porque no existe un procesador que lo ejecute. En vez de eso, el ordenador con el que trabaja el ingeniero transformará ese código (ese fichero de texto) en unas conexiones en la FPGA tales que la FPGA se convierta en un circuito que haga la función que el ingeniero había descrito. Esto, que suena como si el proceso de transformación requiriese mucho ingenio por parte del ordenador, en realidad no lo es tanto. Lo más complicado del proceso no es pasar de la descripción a los bloques electrónicos sino conectarlos de forma que la señal se transmita adecuadamente entre ellos.

Así que, si un ingeniero escribe una descripción de un procesador, por ejemplo un Z80, este procesador podrá realizarse en los circuitos de muchos modelos distintos de FPGA y con el mismo código, también en un chip de verdad. Todos los procesadores y sistemas electrónicos digitales modernos están diseñados utilizando estos lenguajes de descripción (Verilog, principalmente) y el que acaben en un chip dedicado, con conexiones fijas, o en una FPGA, con conexiones variables; depende del tamaño de mercado para el que se fabrican y de su velocidad, fundamentalmente.

Resumiendo: en una FPGA se reconstruye el circuito original y, a efectos electrónicos, nos encontramos ante el mismo sistema que el original. Hasta los detalles más insignificantes y las particularidades más meticulosas de un sistema se pueden recrear en la FPGA y a menudo ocurrirán como consecuencia natural del circuito que se monta en ella. Sin embargo en un emulador el programador necesita elegir qué aspectos de la máquina original va a imitar y de qué manera. Los aspectos que no se noten en la emulación no se replican en un emulador. Sin embargo en la FPGA, por ser el circuito electrónico, es necesario incluir prácticamente todos los elementos que ocurrían en el original.

¿Ventajas de una FPGA frente a un emulador?

A estas alturas debería resultar obvio: la FPGA es, básicamente, el sistema original. Así que comparar el emulador con la FPGA es muy similar a compararlo con el original. Eso sí, la FPGA habitualmente puede hacer cosas que el sistema original no puede:

  • Acceso a más RAM
  • Lectura de discos desde una tarjeta SD
  • Incrementar la velocidad de la CPU o de la memoria
  • Otros trucos dado el acceso directo al hardware del que dispone

Y puede hacer algo que los emuladores tienen casi por imposible: conectarse a hardware real de la época, como el caso de los puertos MIDI del Mist o los puertos de cartucho del One Chip MSX.

Y hasta aquí este repaso sobre FPGAs y emulación.

YM3012 relación entrada/salida medida en silicio

La salida del YM2151 es una señal digital de 16 bits: 3 sin valor, 10 de mantisa y 3 de exponente. Los dos canales (izquierda y derecha) se transmiten secuencialmente. Este formato está relativamente detallado en la hoja de datos del YM2151 y del DAC que lo acompañaba, el YM3012. Sin embargo, dado que los dos chips eran de Yamaha y cuyo uso era ir el uno con el otro, Yamaha nunca explicó demasiado bien cómo funcionaban. Seguramente nadie usó nunca un YM2151 sin un YM3012 y viceversa. Pero poder extraer sonido de un YM2151 sin el YM3012 hace falta saber primero qué hacía este exactamente. Y para eso, nada mejor que unas medidas.

Medidas del YM3012

Con VDD=5.17V realicé medidas para cada valor del exponente (del 1 al 7) y para los siguientes valores de mantisa: $00, $0F, $3F, $FF, $100, $10F, $13F, $1FF, $200, $20F, $21F, $2FF, $300, $30F, $31F, $33F, $3FF. A partir de las medidas, caractericé la relación entrada salida así: Vout = n + m * d. Donde d es el valor de la mantisa y n,m son valores que dependen del exponente según esta relación:

exponente n m
1 2.559 0.039*0.001
2 2.530 0.082*0.001
3 2.493 0.163*0.001
4 2.417 0.314*0.001
5 2.252 0.636*0.001
6 1.932 1.261*0.001
7 1.290 2.522*0.001

Para cada valor de exponente, la m (es decir, la sensibilidad o el tamaño del LSB) cambia en un factor 2. Pero el corte con cero, la n, también varía. La relación es de tal forma que empezando por exp=7 la salida varía desde 1.289V (d=0) hasta 3.870V (d=3FF), para exp=6 el rango se acorta a 1.937 (d=0) hasta 3.220 (d=3FF), para exp=5 el rango es 2.250 (d=0) hasta 2.900 (d=3FF) y así sucesivamente.

Lo que esto significa es que la señal está centrada alrededor de VDD/2. Para exp=7 se accede a todo el rango de señal desde VDD/4 hasta VDD*3/4 con una resolución de 10 bits. Al pasar a exp=6 el rango se acorta de forma simétrica alrededor de VDD/2 y se divide de nuevo en 10 bits. Es decir, la mitad central del rango goza de mayor resolución. Para exp=5 el rango vuelve a dividirse y a aumentar la resolución y así sucesivamente.

Esto explica la implementación del YM3012 en Verilog de Tatsuyuki Satoh. Cuyo meollo es este:


module dac3012(
CLK,CLKEN,
SO,SH1,SH2,
L,R
);
input CLK;
input CLKEN;
input SO,SH1,SH2;
output [15:0] L,R;

/////////////////////////////////////////////////////////////////////////////
// capture
/////////////////////////////////////////////////////////////////////////////
reg [12:0] sreg;
reg last_sh1,last_sh2;

reg [15:0] dr,dl;
reg [ 2:0] sr,sl;

reg [15:0] out_r,out_l;

always @(posedge CLK)
begin
if(CLKEN)
begin
// shift register
sreg <= {SO,sreg[12:1]};

last_sh1 <= SH1;
if(last_sh1 & ~SH1)
begin
out_r <= dr;
sr <= sreg[12:10];
dr <= {~sreg[9],sreg[8:0],6’b000000};
end else if(sr<7)
begin
// increment floating point
sr <= sr + 1;
dr[14:0] <= dr[15:1];
end

last_sh2 <= SH2;
if(last_sh2 & ~SH2)
begin
out_l <= dl;
sl <= sreg[12:10];
dl <= {~sreg[9],sreg[8:0],6’b000000};
end else if(sl<7)
begin
// increment floating point
sl <= sl + 1;
dl[14:0] <= dl[15:1];
end
end
end

/////////////////////////////////////////////////////////////////////////////
// output
/////////////////////////////////////////////////////////////////////////////
assign L = out_l;
assign R = out_r;

endmodule

La inversión del bit D[9] es una forma rápida de sumar 512 al valor para centrarlo en torno a un imaginario VDD/2. Luego divide el valor entre 2 conservando el signo para cada valor de exp distinto de 7.

Esta implementación no se corresponde, creo, fielmente a la relación E/S medida en el chip. En principio, para exp=7 entiendo que no hay que hacer nada especial y se puede guardar el valor tal cual en 16 bits:


linear[15:0] <= { data[9:0], 6'd0 };

Para los demás valores distintos hay que dividir por 2 y sumar 256:


linear[15:0] <= { 10'h100, 6'd0 } + { 1'b0, linear[15:1] };

en iteraciones sucesivas a la manera de actuar de Satoh.

Flanco de las señales del YM2151

Después de un año sin apenas tocar el proyecto de clonar el YM2151 en FPGA, vuelvo a la carga con una plaquita nueva con conversores 3V-5V que evitan los problemas de la anterior placa. Ahora que la comunicación entre FPGA e YM2151 ya está garantizada llega el turno de empezar a sacar información para hacerlo sonar a través del kit Spartan-3 de Xilinx.

Lo primero que hace falta tener claro es si la salida del YM2151, el pin SO (Sound Out, a veces descrito como SD Sound Data) hay que recogerlo en el flanco positivo o negativo del reloj de salida (pin P1). Para ello he usado el osciloscopio USB Analog Discovery (que recomiendo vivamente) y he podido medir las salidas del chip sin programar (ver imagen).

Hay dos canales (izquierdo y derecho) cuya información se envía por el pin SO multiplexada. El flanco de bajada de SH1 indica que los datos del canal 1 empiezan. Los tres primeros bits se descartan. Luego viene el dato (D9-D0, 10 bits) y finalmente el exponente (tres bits). En total 16 bits por canal (13 de información y 3 de relleno)

Las señales de salida del YM2151 (SO, SH1 y SH2) están sincronizadas con el flanco negativo del P1

Las señales de salida del YM2151 (SO, SH1 y SH2) están sincronizadas con el flanco negativo del P1

Eliminación del autodisparo en un Quickshot

Hace unos días compré por eBay estos mandos, aun precintados, de Quickshot (ver aquí).

Imagen

El problema de estos mandos es que llevan un autodisparo en los dos botones. Hay muchos juegos en los que el autodisparo es un inconveniente: no permite realizar acciones o estorba. Así que me propuse quitárselo. Los dos botones en realidad son el mismo disparo para el ordenador. El cambio que voy a enseñar consiste en dejar el izquierdo con autodisparo y el derecho sin él. Después de desmontar la caja con mucha facilidad hice un diagrama de la placa:

Imagen

Y de ahí, se saca el esquema del circuito. Consiste en un oscilador conectado a los dos botones. Los botones se conectan entre un diodo LED y la salida del oscilador. La placa está preparada para dar salida individual a cada botón pero el cable carece de la conexión para el 2º botón. Para eliminar el autodisparo en uno de los botones basta con desconectar el lado del botón que va al colector del transistor y conectarlo a masa (cable negro) en su lugar. En el esquema esta conexión está indicada en negro.

Imagen

Para conectar a masa, usé el emisor del transistor más cercano al botón, que ya estaba conectado a masa.

Imagen

Y con eso queda el cambio hecho.

Conexión de la placa Spartan 3A Starter Kit a Linux

Llegó por fin la FPGA y lo primero ha sido intentar programarla desde Linux. Yo gasto Fedora 17 (voy una versión atrasado), con una versión de núclero 3.x. Con la placa viene la versión 13.4 de ISE (la herramienta de Xilinx). Durante la instalación aparece un error acerca del controlador del cable, un tal windrvr. Rebuscando entre los ficheros de C, parece ser que está pensado para la versión 2.6 del núcleo exclusivamente. Buscando por aquí y sobre todo por allá, acabo enterándome de lo que pasa:

Resulta que no hace falta el windrvr, con que esté libusb instalado basta. Pero, al conectar el cable Linux debe instalarle el firmware. Eso se hace con el demonio udev. El programa de instalación de ISE ya lo ha dejado todo casi hecho en el directorio /etc/udev/rules.d pero con una sintáxis incorrecta. Parece ser que a algún iluminado se le ocurrió que había que cambiar BUS por SUBSYSTEM, SYSFS por ATTR, y TEMPNODE a minúsculas: tempnode. A saber la de gente que estará mareada por culpa del tipo ese.

El fichero /etc/udev/rules.d/xusbdfwu.rules corregido para mi cable queda así:

ATTR{idVendor}=="03fd", ATTR{idProduct}=="0008", MODE="666"
SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="03fd", ATTR{idProduct}=="000d",
RUN+="/sbin/fxload -v -t fx2 -I /usr/share/xusb_emb.hex -D $tempnode"

El fichero dice que cuando se conecte al USB el dispositivo 03FD:000D, se ejecute el comando fxload, que es el que carga el firmware. Con el nuevo firmware el dispositivo pasa a ser reconocido como 03FD:0008 y en ese caso se aplica la primera línea, que hace referencia a los permisos. Para que ISE pueda acceder al cable hay que darle permiso a todo el mundo. Eso se hace con la línea acabada en MODE=”666” del fichero. Aun así, si se está usando SELinux no funciona. Seguro que se puede añadir la excepción para SELinux lo acepte pero yo he preferido pasar SELinux al modo permisivo.

Tras hacer todas estas cosas, el cable funciona correctamente con ISE en Linux 3.x.

Las tonadillas del 1942

Después de presentar el AY-3-8910 y explicar su diseño, voy a pasar a un ejemplo de uso. Ya sabéis que es el chip de música de varios ordenadores de 8 bits y de placas arcade. Ahora vamos a hacerlo sonar en la placa del 1942 de Capcom.

Las máquinas de arcade usan generalmente dos procesadores: uno para el juego y otro para el sonido. La comunicación suele ser muy sencilla: hay una posición de memoria en la que el procesador principal escribe un valor. El procesador de sonido comprueba el valor a intervalos regulares (240 Hz en el caso del 1942) y reproduce el efecto sonoro o la melodía correspondiente.

En este caso, como sólo se trata de comprobar si el AY-3-8910 diseñado suena cómo debe, no hace falta modelar toda la placa, basta con la parte sonora. Es más, el código sonoro lo podemos dejar fijo y cambiarlo a mano cuando queramos probar otro sonido. La memoria que guardaba el código se implementaba con un latch de 8 bits. El modelo en Verilog es:

module SOUND_LATCH(
  output reg [7:0] dout,  
  input enable,
  input clk,
  input rd_n );
 
wire sample = clk & ~rd_n;
reg [7:0]data;
 
initial begin
    dout=0;
    data=0;
    #100e6 data=8'h12; // enter the song/sound code here
end
 
always @(posedge sample) begin
  if( !enable )
        dout=0;
  else begin 
    if( !rd_n ) begin
            dout=data;
        end
  end
end
endmodule

El resto del código Verilog que modela la placa de 1942 está aquí.

El procesador Z80 escogido es el tv80, que ha funcionado bien en estas simulaciones. Para que el programa del Z80 lea el código debe recibir interrupciones. Eso lo hacemos con un reloj fijo:

always begin // interrupt clock 
int_n=1; 
  forever begin       
   #(4166667-int_low_time) int_n=0; // 240Hz        
  #(167*2*80) int_n=1;    
  end
end

Y lo más importante: hay que guardarse la salida de sonido. Para ello tomamos muestras a 44.1kHz y producimos salida por pantalla. Como 1942 tenía dos chips AY, escribims cada salida en una línea distinta:

always #22676 begin // 44.1kHz sample    
    $display("%d", amp0_y*10 ); 
    $display("%d", amp1_y *10);         
end

Cuando simulemos el Verilog, hay que hacerlo grabando la salida de texto a un fichero para luego convertir esas muestras a un fichero WAV y poder oírlas. Luego usamos este programa para hacer la conversión. Si se hace en Linux se puede usar una tubería para ahorrarse el fichero intermedio.

Y aquí se acaba la entrada de hoy. Hay varios temas posibles para la siguiente entrada: tenemos ya el ADPCM (el CODEC de audio usado para voces digitalizadas en los 90) medio funcionando y hay progresos también con el YM2203. La placa de trabajo, una Spartan 3A starter kit, nos llega mañana así que ¡hay mucho por probar!

El AY-3-8910 y SQMusic

Mi periplo por el universo de la música electrónica empieza por el AY-3-8910. Este chip estaba en mi primer ordenador, el Amstrad CPC 464, y a pesar del salto a los 16 bits, en mi segundo ordenador, el Atari STe. Este chip era un PSG (Programmable Sound Generator) y hacía una cosa muy sencilla: ondas cuadradas a una frecuencia que era fracción de la de entrada. Es decir: un contador programable. En realidad tenía un poco más: podía modificar la entrada según otra función llamada envolvente. Esta consistía en una sierra en todas sus variantes: un diente, muchos dientes, rampa arriba y caída abrupta, rampa arriba y abajo, etc. Había tres canales simultáneos pero todos compartían la misma envolvente, así que esta tenía un uso limitado. Cada canal tenía un control de ganancia independiente, siempre que no se usase la envolvente. Además había un generador de ruido, para imitar percusiones, explosiones, etc. Todo parece muy sencillo, pero el chip ofrecía dos facilidades a los ingenieros de la época:

  1. El programador no tenía que preocuparse de alimentar constantemente a un DAC (Digital Audio Converter) para producir el sonido, algo además difícil de hacer con procesadores lentos.
  2. El diseñador del equipo se ahorraba los DAC de sonido porque estaban integrados en el AY-3-8910.

Este chip fue un éxito. Estuvo en multitud de sistemas, muchos de ellos muy populares y marcó el nivel mínimo a cumplir. Los demás chips que irán apareciendo en el blog son compatibles con este.

Siendo este el procesador de sonido más básico y fundamental, mi trabajo empieza por él. Voy a repasar ahora el código disponible en opencores para implementar la funcionalidad de sonido digital. Lo primero es hacer el divisor de reloj que se usa para generar los tres canales de audio y el reloj del módulo de ruido:

always @(posedge clk or negedge reset_n) begin
  if( !reset_n) begin
    count<=0;
    clkdiv<=0;
  end
  else begin
    if( period==0 ) begin
      clkdiv<=0;
      count<=0;
    end
    else if( count >= period ) begin
        count <= 0;
        clkdiv <= ~clkdiv;
      end
      else count <= count+1;
  end
end

Como veis, no hay nada de especial: se conmuta la salida si se llega a la cuenta programada por el usuario y si no, sólo se cuenta.

El generador de ruido responde a su propio reloj modificando un polinomio de 17 bits con un par de XOR. Esto genera un patrón muy largo que es pseudoaleatorio y que suena, bueno, ¡pues a ruido!

always @(posedge noise_clk or negedge reset_n) begin
  if( !reset_n) begin
    poly17<=0;
  end
  else begin
     poly17<={ poly17[0] ^ poly17[2] ^ poly17_zero, poly17[16:1] };
  end
end

El código que necesita pensar más es el del generador de envolvente. Las formas que ofrece el chip son muy variadas y hacerlas todas sin complicarse excesivamente la vida no es trivial. Estas son las formas:

Imagen

y este es el código que las implementa:

always @(posedge clk or negedge reset_n) begin
  if( !reset_n) begin
    gain<=4'hF;
    dir<=0;
    prev_ctrl<=0;
    stop<=1;
  end
  else begin
    if (ctrl!=prev_ctrl) begin
      prev_ctrl<=ctrl;
      if( ctrl[2] ) begin 
        gain<=0;
        dir<=1;
        stop<=0;
      end
      else begin
        gain<=4'hF;
        dir<=0;
        stop<=0;
      end
    end
    else begin 
      if (!stop) begin
        if( !prev_ctrl[3] && ((gain==0&&!dir) || (gain==4'hF&&dir))) begin
          stop<=1;
          gain<=0;
        end
        else begin
          if( prev_ctrl[0] && ( (gain==0&&!dir) || (gain==4'hF&&dir))) begin // HOLD
            stop<=1;
            gain <= prev_ctrl[1]? ~gain : gain;
          end 
          else begin
            gain <= dir ? gain+1 : gain-1;          
            if( prev_ctrl[1:0]==2'b10 && ( (gain==1&&!dir) || (gain==4'hE&&dir)))
              dir <= ~dir; // ALTERNATE
          end
        end
      end
    end
  end
end

Se usa una variable para almacenar el sentido de la cuenta (dir) y otra para saber si hay que detenerse (stop). Notad las comparaciones que se hacen con cuentas que no son la extrema (4’hE) para que contando un ciclo de reloj adicional la onda quede en su sitio.

Con esto, el AY-3-8910 está prácticamente hecho. En la próxima entrada hablaré sobre cómo simular el circuito y escuchar la música en el ordenador, sin necesidad de implementarlo en FPGA o silicio.

Referencias: AY-3-8910 Application Notes