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

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: