Problema del código Verilog del escáner del teclado con la máquina de estado y la entrada de columna

1

Estoy desarrollando un teclado tanto en hardware como en Verilog usando una placa DE2 Cyclone II. Hice un teclado usando botones (interruptores) que siguen este esquema:

El escáner funciona configurando las entradas de Columna todas en ALTO (1) en el estado inicial, y cuando se detecta una pulsación de tecla, escanea cada columna individualmente y cuando detecta que una de las filas tiene un valor lógico ALTO, Se procederá a decodificar qué tecla se ha pulsado. Probé el circuito físico usando sondas lógicas y está funcionando correctamente.

Creé un programa Verilog que detectaría la tecla presionada, y también explicaría el efecto de rebote de los botones. El problema es que cuando conecto los cables a los pines GPIO correctos del encabezado de Expansión de la placa DE2 y pruebo mi programa, parece que realiza el siguiente comportamiento: No importa qué tecla en qué columna presiono, la Máquina del Estado. comienza en el estado inicial S1 (C [2: 0] = 111), avanza a S2 cuando se presiona una tecla (C [2: 0] = 001), continúa captando una señal alta en K (resultado de | R [ 3: 0]) y procede a S5, el estado final donde se decodifica la clave.

Por ejemplo, cualquier tecla presionada genera la siguiente salida:

Cualquier tecla presionada en Row0: KeyValue = 1.

Cualquier tecla presionada en Row1: KeyValue = 4.

Cualquier tecla presionada en Row2: KeyValue = 7.

Cualquier tecla presionada en Row2: KeyValue = A.

El comportamiento esperado es que continuará escaneando la Columna 2 (C [2: 0] = 010) o la Columna 3 (C [2: 0] = 100) si es necesario, ya que se podrían presionar las teclas de estas columnas. Desafortunadamente, esto no sucede.

No estoy seguro de cuál es el problema. Revisé el código varias veces y no veo la causa. Siento que es un problema de tiempo, ¿alguien podría sugerir alguna forma de mejorar mi código (especialmente la máquina de estados finitos en keyscan )?

    /* KEYPAD MATRIX Scanner, Debouncer, and Decoder. */
    //DE2 Board Expansion pinout: http://www.johnloomis.org/altera/DE2/expansion.html
    //Source:                                                                                                                                                 https://www.allsyllabus.com/aj/note/ECE/Digital_System_Design_Using_VHDL/Unit3/Design%20of%20a%20keypad%20scanner.php#.VQYExI7F-So
    //Similar: http://www.uccs.edu/~gtumbush/4200/lab5.pdf
module keypad (CLOCK_50, GPIO_0, LEDG, HEX0);
    input CLOCK_50; 
    inout [23:11] GPIO_0; // //changing R input values
    output [1:0] LEDG;
    output [0:6] HEX0;

    wire KeyPress; //valid key press detected
    wire [3:0] KeyValue;
    /*
    output KeyPress; //valid key press detected
    output [3:0] KeyValue;
    */

    wire K, Kd; //for debouncer circuit
    wire Q1; //identifies whether current state is S5 (idle).
    wire [2:0] C; //Column output pin (for scanner circuit)

    wire [3:0] R;
    assign R[3] = GPIO_0[17];
    assign R[2] = GPIO_0[15];
    assign R[1] = GPIO_0[13];
    assign R[0] = GPIO_0[11];

    debounce(R, Q1, Kd, K, CLOCK_50);
    keyscan(Kd, K, CLOCK_50, KeyPress, C, Q1);
    decoder(R, C, KeyValue, KeyPress);

    //assign C to output pins...
    assign GPIO_0[19] = C[0];
    assign GPIO_0[21] = C[1];
    assign GPIO_0[23] = C[2];

    assign LEDG[0] = KeyPress;
    seven_seg_decoder(KeyValue, HEX0);

endmodule

/* Decoder is solely a combinational circuit (no flip-flops).
Its output will change as the keypad is scanned. At the time a valid key
is pressed (V=1), its output will have the correct value, and may be saved 
in a register elsewhere. */
module decoder(R, C, N, V);
    input [3:0] R; //active rows in key matrix
    input [2:0] C; //active columns in key matrix
    input V;
    output reg [3:0] N; //key number


    /*FIXED logic: Last two encoded buttons changed FROM [0000, 1011] TO [1011, 1100]*/
    always @(posedge V) begin 
        N[3] = R[3] | (R[2] & ~C[0]);
        N[2] = R[1] | (R[2] & C[0]) | (R[3] & C[2]);
        N[1] = (R[0] & ~C[0]) | (R[3] & ~C[2]) | (R[1] & C[2]) | (R[2] & C[0]);
        N[0] = (C[1] & (R[1] | R[3])) | (~C[1] & (R[0] | R[2]));
    end

    /* OLD LOGIC
    assign N[3] = (R[2] & ~C[0]) | (R[3] & ~C[1]);
    assign N[2] = R[1] | (R[2] & C[0]);
    assign N[1] = (R[0] & ~C[0]) | (~R[2] & C[2]) | (~R[1] & ~R[0] & C[0]);
    assign N[0] = (R[1] & C[1]) | (~R[1] & C[2]) | (~R[3] & ~R[1] & ~C[1]); */
endmodule

//Example: http://www.fpga4fun.com/Debouncer2.html
module debounce(R, Q1, Kd, K, Clk_50);
    input [3:0] R; //activated rows of keypad
    input Q1; //is scanner in S5 state? 1 for true, 0 for false.
    input Clk_50;
    output K; //immediate Key detector (primarily used for scan)
    output Kd; //debounced Key

    wire dClk;
    clock_50M_toX(Clk_50, dClk);

    assign K = |R; //binary OR reductor 
    wire DA;
    wire QA;
    assign DA = (QA & ~Q1) | K;

    D_flipflop(DA, dClk, QA);
    D_flipflop(QA, dClk, Kd);
endmodule



//Key scanner FSM
//Modeled on following: http://www.asic-world.com/verilog/memory_fsm3.html
module keyscan(Kd, K, Clk, V, C, Q1);
    input Kd, K;
    input Clk; //Clock - 50MHz
    output reg [2:0] C;
    output reg V;
    output reg Q1; //top state bin value (X6)
    /* Q1 used in debounce circuit to provide extra security 
    blanket for K input (in case of error or delay with K) */

/**
State assignment (One-Hot):
S1 = 00001  START state
S2 = 00010  
S3 = 00100  
S4 = 01000  
S5 = 10000  IDLE/DEST state
State transition table:
State Bin       Kd  K       NextBin Next
S1      00001       1   X       00010       S2
                    0   X       00001       S1
S2      00010       X   0       00100       S3
                    X   1       10000       S5
S3      00100       X   0       01000       S4
                    X   1       10000       S5
S4      01000       X   0       10000       S5 <-shouldn't ever happen, but JIC
                    X   1       10000       S5
S5      10000       1   X       10000       S5
                    0   X       00001       S1
ERR 00000       X   X       00000       WAIT <- Just for fun
**/
//Mealy + Moore FSM
    reg [4:0] state = 5'b00001;
    reg [4:0] next_state;
    initial begin //SET starting state
        state = 5'b00001;
    end
    /*
    assign state[0] = (state[0] & ~Kd) | (state[4] & ~Kd);
    assign state[1] = state[0] & Kd;
    assign state[2] = state[1] & ~K;
    assign state[3] = state[2] & ~K;
    assign state[4] = (state[1] & K) | (state[2] & K) | state[3] | (state[4] & Kd);
    assign Q1 = state[4]; //X6*/

    always @ (state or Kd or K) begin
        next_state[0] = (state[0] & ~Kd) | (state[4] & ~Kd);
        next_state[1] = state[0] & Kd;
        next_state[2] = state[1] & ~K;
        next_state[3] = state[2] & ~K;
        next_state[4] = (state[1] & K) | (state[2] & K) | state[3] | (state[4] & Kd);
        Q1 = next_state[4];
    end

    always @(posedge Clk) begin
        state <= next_state;
        case (state)
            5'b00001: begin //S1
                V = 1'b0;
                C <= 3'b111;
            end
            5'b00010: begin //S2
                C <= 3'b001;
            end
            5'b00100: begin //S3
                C <= 3'b010;
            end
            5'b01000: begin //S4
                C <= 3'b100;
            end
            5'b10000: begin //S5
                //***while here, don't change C.
                V <= 1'b1;
            end
        endcase
    end

endmodule


/* Manually adjust values here depending on bounce time */
module clock_50M_toX(Clk_50MHz, dClk);
    input Clk_50MHz;
    output dClk;
    reg pulse_XHz = 1; //start off high (w/0 registering "posedge")
    /**
    Formula for calculating bit length of counter.
    Say bounce time = Xms.
    Then num pulses of 50MgHz gone by for entire duration of Xms:
        n = (X/1000) * (50 *10^6)
    We want to simulate the rising and falling edge of the pulse,
    so we mark the halfway pulse point like so:
        n_half = n / 2
    Then the length of the counter to hold the correct pulses
    and toggle the clock is:
        c = ceil(log2(n_half)) 
        => reg [c-1:0] clock_pulses!!
        => if (clock_pulses < ((n_half) - 1)) begin
    **/
    reg [16:0] clock_pulses; //try ~3ms
    always @ (posedge Clk_50MHz) begin
            //Marking the halfway point in order to simulate
            //the rising and falling edge of the 1Hz clock pulse.
            if (clock_pulses < ((75000) - 1)) begin
                clock_pulses <= clock_pulses + 1;
            end else begin
                clock_pulses <= 0;
                pulse_XHz <= ~pulse_XHz;
            end
    end
    assign dClk = pulse_XHz;
endmodule


/* positive edge triggered D_FF without reset */
module D_flipflop(d, clk, q);
    input d, clk;
    output q;
    reg q;
    always @(posedge clk) begin
        q <= d;
    end
endmodule

//hexadecimal convertor: converts any value from 0 to 15
//hexadecimal: a consequetive group of 4 binary digits can be considered independently, and converted directly
module seven_seg_decoder(V, Display);
    input [3:0] V; //input code
    output [0:6] Display; //output 7-seg code

    //When you want to turn on a segment, set it low
    assign Display[0] = (~V[3] & ~V[1] & (V[2] ^ V[0])) | (V[3] & V[0] & (V[2] ^ V[1]));
    assign Display[1] = (~V[3] & V[2] & ~V[1] & V[0]) | (V[3] & V[2] & ~V[0]) | (V[3] & V[1] & V[0]) | (V[2] & V[1] & ~V[0]);
    assign Display[2] = (~V[0] & ((V[3] & V[2] & ~V[1]) | (~V[3] & ~V[2] & V[1]))) | (V[3] & V[2] & V[1]);
    assign Display[3] = (~V[3] & ~V[1] & (V[2] ^ V[0])) | (V[3] & ~V[2] & V[1] & ~V[0]) | (V[2] & V[1] & V[0]);
    assign Display[4] = (~V[3] & V[2] & ~V[1]) | (V[0] & ((~V[2] & ~V[1]) | ~V[3]));
    assign Display[5] = (V[3] & V[2] & ~V[1] & V[0]) | (~V[3] & ~V[2] & (V[1] | V[0])) | (~V[3] & V[1] & V[0]);
    assign Display[6] = (V[3] & V[2] & ~V[1] & ~V[0]) | (~V[3] & V[2] & V[1] & V[0]) | (~V[3] & ~V[2] & ~V[1]);
endmodule
    
pregunta roro172

1 respuesta

1

Esto es más un comentario extendido que una respuesta, porque no puedo poner ejemplos de código en un comentario.

Verilog le da mucho poder para expresar la lógica de la forma más adecuada; no es necesario reducir todo a las ecuaciones lógicas booleanas. Por ejemplo, su convertidor de 7 segmentos se expresaría más claramente como una declaración de caso:

module seven_seg_decoder (V, Display);
  input [3:0] V; //input code
  output [0:6] Display; //output 7-seg code

  // When you want to turn on a segment, set it low
  always @(*) begin
    case (V)
    4'h0: Display <= 7'b0000001;
    4'h1: Display <= 7'b1001111;
    4'h2: Display <= 7'b0010010;
    // ... etc.
    endcase
  end
endmodule

Una declaración de caso también es una buena manera de expresar la funcionalidad de una máquina de estado, también. Si desea que otros ingenieros puedan leer y comprender su código, debe usar las construcciones de más alto nivel que tengan sentido.

    
respondido por el Dave Tweed

Lea otras preguntas en las etiquetas