pub:dglab_qfsm

La aplicación Qfsm nos permitirá diseñar y analizar con detalle máquinas de estado tanto de tipo Mealy como de Moore, o mixtas. Al crear una máquina de estados, se solicita información sobre el numero de entradas y el número de salidas y el tipo de las mismas. Deben darse nombres a las mismas para poder recordar lo que significan cada una.

La imagen muestra la ventana de edición de propiedades de una máquina de estados binaria. La elección de maquina de estados binaria significa que tanto en las transiciones como en estados, solo se podrán insertar valores binarios. Esto se puede configurar a ASCII o incluso texto libre (del cual no será posible obtener una descripción hardware). Las variables de entrada y salida deben estar separadas por comas y se debe indicar el número.

Como ejemplo, vamos a realizar el diseño de una máquina de estados para el control de un multiplicador serie por sumas repetidas. En este caso, las señales de control serán:

Inputs:
  Start (Comienza la multiplicación)
  A_eq_Z (Comprueba si el primer operando es cero)
  B_eq_Z (Comprueba si el segundo operando es cero)
  count_eq_Z (Comprueba si el contador ha llegado a cero)
Moore outputs:
  Clr_aux (pone a cero el registro auxiliar)
  Rdy (Indica cuándo el multiplicador está listo)
  reg_inputs (indica el momento de registrar las entradas)
  reg_outputs (indica el momento de registrar las salidas)

A pesar de que algunas señales no tengan sentido aún, rellenad los campos del menú de propiedades de la máquina con las entradas y salidas mostradas. El diseño de la máquina de estados se puede observar en la siguiente figura, donde se han colocado estados y transiciones para formarla y definirla de forma inequívoca.

Para definir los estados podemos hacer clic sobre el icono marcado de nuevo estado. En el se podrá indicar el nombre del estado, el código binario asociado al estado, así como las salidas para el estado. Se pueden editar otra serie de parámetros que se muestran en la figura. El uso de la descripción de estados es útil para recordar las funciones que cumple el estado. El estado idle se identifica con el código 00, y las salidas son 1100, que se encuentran ordenadas en el orden en el que se introdujeron en la máquina de estados. En este caso, las salidas activas son Clr_aux y Rdy. La siguiente imagen mostrará la edición de la transición desde idle a init. Se pueden configurar las condiciones para que se produzca la transición. En este caso, para que se produzca la transición debe activarse la señal Start, sin importar el valor de las demás entradas. Por otro lado, el cuadro de outputs contendría los valores de las salidas de Mealy en el caso de que los haya. Para nosotros, dado que la máquina es de Moore, no los necesitamos. Veamos cómo se puede simular la máquina de estados. Para ello, vamos al menú:

Machine -> Simulate

En el siguiente gif animado se muestra un ejemplo de simulación de la máquina de estados.

Se demuestran las transiciones sin problema, la máquina de estados funciona como deseamos.

Descripción del funcionamiento y objetivo de la máquina de estados

La idea de esta máquina de estados es registrar pasar a init desde idle cuando se pulsa Start. En init, se deberá comprobar si alguno de los operandos es nulo, a la vez que se registran los mismos, para operar con ellos. Si son distintos de cero pasamos a calcular, donde esperamos hasta que se terminan las sumas repetidas, señal dada por count_eq_Z. En este caso, pasamos al estado done en el que esperamos hasta que se retire la señal Start, si aún permanece activa. Una vez se desactiva, volvemos al estado inicial idle.

  • La señal Clr_aux se activa en el estado idle para mantener el acumulador a cero.
  • La señal Rdy se activa en el estado idle, indicando la disponibilidad del módulo.
  • La señal reg_inputs se activa en el estado init, e indica el registro de las entradas (operandos).
  • La señal reg_outputs se activa en el estado calculate, e indica el registro de la salida del acumulador hasta el instante dado, que será correcta cuando se salga del estado.

Simulación con TkGate y hardware del multiplicador

El ejemplo de la máquina de estados se puede llevar a su aplicación dentro del multiplicador, exportando directamente el diseño desde Qfsm a una máquina de estados escrita en Verilog. Para ello, deberemos ir al menú:

File -> Export -> Verilog HDL
FSM_multiplier.v
// This file was generated by
// Qfsm Version 0.54
// (C) Stefan Duffner, Rainer Strobel
 
module FSM (clock, reset, in, state, moore);
  input     clock, reset;
  input  [3:0]  in;
  output [3:0]  moore;
  output [1:0]  state;
 
  reg[3:0]      moore;
  reg[1:0]      state, nextstate;
 
  parameter idle = 2'b00, init = 2'b01, calculate = 2'b10, done = 2'b11;
 
  always @ (posedge reset or posedge clock) begin
    if (reset)
      begin
        state <= idle;
        moore <= 4'b1100;
      end;
    else
      begin
        state <= nextstate;
        case (nextstate)
          idle:
            moore <= 4'b1100;
          init:
            moore <= 4'b1010;
          calculate:
            moore <= 4'b0001;
          done:
            moore <= 4'b0000;
        endcase
      end
  end
 
  always @ (in or state) begin
      nextstate = state;
      case (state)
        idle:
        begin
          if (in==4'b1000 ||
              in==4'b1100 ||
              in==4'b1010 ||
              in==4'b1001 ||
              in==4'b1110 ||
              in==4'b1101 ||
              in==4'b1011 ||
              in==4'b1111)
          begin
            nextstate = init;
          end
        end
        init:
        begin
          if (in==4'b0000 ||
              in==4'b1000 ||
              in==4'b0001 ||
              in==4'b1001)
          begin
            nextstate = calculate;
          end
          else if (in==4'b0010 ||
              in==4'b1010 ||
              in==4'b0110 ||
              in==4'b0011 ||
              in==4'b1110 ||
              in==4'b1011 ||
              in==4'b0111 ||
              in==4'b1111)
          begin
            nextstate = done;
          end
          else if (in==4'b0100 ||
              in==4'b1100 ||
              in==4'b0110 ||
              in==4'b0101 ||
              in==4'b1110 ||
              in==4'b1101 ||
              in==4'b0111 ||
              in==4'b1111)
          begin
            nextstate = done;
          end
        end
        calculate:
        begin
          if (in==4'b0001 ||
              in==4'b1001 ||
              in==4'b0101 ||
              in==4'b0011 ||
              in==4'b1101 ||
              in==4'b1011 ||
              in==4'b0111 ||
              in==4'b1111)
          begin
            nextstate = done;
          end
        end
        done:
        begin
          if (in==4'b0000 ||
              in==4'b0100 ||
              in==4'b0010 ||
              in==4'b0001 ||
              in==4'b0110 ||
              in==4'b0101 ||
              in==4'b0011 ||
              in==4'b0111)
          begin
            nextstate = idle;
          end
        end
      endcase
  end
 
endmodule

Como se puede observar del código resultante, la descripción de la máquina de estados es muy verbosa y en general compleja de comprender por ello mismo, dada la longitud de código de una máquina tan simple. En cualquier caso, esta máquina de estados puede integrarse directamente en un diseño de un multiplicador que podría tener la siguiente descripción hardware:

multiplier.v
module multiplier(Ck, NRESET, A, B, C, Start, Rdy);
 
  input [7:0] A, B;
  input Start;
  input Ck, NRESET;
  output [15:0] C;
  output Rdy;
 
  wire reg_inputs, reg_outputs;
 
  ///////////////////////////////
  //// valor de A registrado ////
  ///////////////////////////////
  reg [7:0] A_reg;
  always@(posedge Ck or negedge NRESET)
    if (~NRESET)
      A_reg <= 0;
    else if (reg_inputs)
      A_reg <= A;
 
  ////////////////////
  //// Acumulador ////
  ////////////////////
  reg [15:0] Aux_reg;
  wire Clr_aux;
  always@(posedge Ck or negedge NRESET)
    if (~NRESET)
      Aux_reg <= 0;
    else if (Clr_aux)
      Aux_reg <= 0;
    else
      Aux_reg <= Aux_reg + A_reg;
 
  ///////////////////////////////////////////
  //// Contador (registra el valor de B) ////
  ///////////////////////////////////////////
  reg [7:0] counter;
  always@(posedge Ck or negedge NRESET)
    if (~NRESET)
      counter <= 0;
    else if (reg_inputs)
      counter <= B;
    else
      counter <= counter -1;
 
  /////////////////////////////////
  //// Instanciación de la FSM ////
  /////////////////////////////////
  wire A_eq_Z = !A; // Señales a proporcionar
  wire B_eq_Z = !B;
  wire count_eq_Z = !counter;
 
  wire [1:0] state;
  FSM fsm(.clock(Ck),
          .reset(~NRESET), // Asume un reset a nivel alto, por lo tanto, invertimos la señal.
          .in({Start, A_eq_Z, B_eq_Z, count_eq_Z}), // Las señales aparecen en el orden introducido inicialmente
          .state(state), // Estados como salidas, aunque no sean necesarios en este caso.
          .moore({Clr_aux, Rdy, reg_inputs, reg_outputs}) // Lo mismo ocurre para las salidas.
  );
  /////////////////////////////
  //// Salidas registradas ////
  /////////////////////////////
  reg [15:0] C;
 
  always@(posedge Ck or negedge NRESET)
    if (~NRESET)
      C <= 0;
    else if (reg_outputs)
      C <= Aux_reg;
 
endmodule

Para testar el sistema a se ha decidido no utilizar las herramientas de interfaz integradas en TkGate, sino hacer una simulación tradicional por medio de un testbench para el módulo. Para transmitir las señales entre un módulo y su test_bench, en TkGate, el segundo debe tener los mismos puertos intercambiando las salidas por entradas, y deben interconectarse directamente. Esto se muestra en la siguiente imagen:

El código para el testbench del multiplicador se muestra a continuación:

multiplier_tb.v
module multiplier_tb(A, B, Ck, NRESET, Start, Rdy, C);
  // Entradas
  input [15:0] C;
  input Rdy;
  // Salidas
  output reg [7:0] A, B;
  output reg Ck, NRESET, Start;
 
  // Generación del reloj
  initial forever #50 Ck = ~Ck;
 
  initial
    begin
      // Valores iniciales
      A = 0;
      B = 0;
      Ck = 0;
      NRESET = 0;
      Start = 0;
 
      // Reset general
      #100 NRESET = 1;
 
      // Multiplicación
      #100;
      A = 8'haa;
      B = 8'h0e;
      Start = 1;
      #100 Start = 0;
 
      // Parada
      @(Rdy)
        $stop;
    end
endmodule

Para simular el sistema simplemente deberemos seleccionar la pestaña de simulación y hacer doble click en el esquemático sobre las señales que se quieren visualizar. En este caso, aparece la figura de una sonda de un analizador lógico sobre la conexión, y se abre la ventana del analizador lógico en el que se pueden visualizar los resultados a lo largo del tiempo. Una vez seleccionados todos, simplemente se inicia la simulación, como se vio anteriormente en el capitulo dedicado a TkGate.

Un detalle de importancia es que las conexiones son nombrados directamente por TkGate, con nombres poco inteligibles, y por tanto conviene cambiarlos. Para ello, seleccionada la pestaña de edición, hacemos doble click sobre la señal deseada y debe aparecer la siguiente ventana:

Sólo es necesario editar el nombre que aparece en la ventana correspondiente a la net seleccionada.

Los resultados de la simulación se muestran en el analizador lógico en la siguiente ventana:

De este modo, podemos comprobar que el cálculo es correcto, y que el módulo se comporta de la forma esperada.

  • pub/dglab_qfsm.txt
  • Última modificación: 2020/09/28 11:44
  • (editor externo)