Archivos del mes: 28 mayo 2013

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

Declaración de intenciones

Este blog va de electrónica aplicada a sistemas retro, es decir máquinas que llevan más de 10 años fuera del mercado. El énfasis está, como no podía ser de otro modo, en sistemas de 8 y 16 bits y habrá un poco de todo: electrónica analógica, digital, programación y lo que haga falta para divertirse con problemas técnicos en torno a lo retro.