Publicaciones de la categoría: Verilog

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