El transmisor Verilog UART envía bytes fuera de servicio

Tengo el siguiente código Verilog que envía 8 bytes al puerto serial sucesivamente después de presionar un botón.

El problema es que los bytes se envían fuera de orden en cuanto a lo que esperaría.

Por ejemplo, si envío los bytes 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, la PC obtiene 0xEF, 0xAD, 0xEF, 0xAD y, a veces, no recibe el resto y cuelga.

He mirado este código muchas veces y parece que no puedo entender por qué podría ser. ¿Estoy usando la selección parcial incorrectamente? No lo creo, pero no sé qué más puede ser el problema.

Adjunté una segunda versión de este código que funciona (lo que significa que se reciben todos los bytes y en el orden correcto), pero requiere un ciclo de reloj adicional porque actualiza los datos en el registro de desplazamiento después de cada transmisión. Si puede ver alguna razón por la cual la primera versión publicada no funciona, pero la segunda sí, ¡hágamelo saber!

module transmission_test_2(sysclk, rxd, txd, LED, button);

input sysclk, rxd, button;
output txd;
output reg LED;

wire receiving_complete, isReceiving, isTransmitting, isError;
reg begin_transmit;
reg [7:0] tbyte;

wire [7:0] rbyte;

reg [2:0] state;

reg [63:0] plain_text;

integer byteN;


parameter IDLE = 0, BEGIN_TRANSMISSION = 1, UPDATE_DATA = 2, SEND_BYTES = 3;


uart uart1(
.clk(sysclk),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_receiving(isReceiving), 
.is_transmitting(isTransmitting),
.recv_error(isError)
);


always @(posedge sysclk)
begin
    begin_transmit = 1'b0;
    case(state)
    IDLE: begin
        if(button==1'b0) begin
            LED = 1'b1;
            plain_text = 64'hDEADBEEFDEADBEEF;
            state = BEGIN_TRANSMISSION;
        end else begin
            LED <= 1'b0;
        end
    end
    BEGIN_TRANSMISSION: begin
        tbyte = plain_text[7:0];
        begin_transmit = 1'b1;
        byteN = 1;
        state = SEND_BYTES;
    end
    SEND_BYTES: begin
        if(!isTransmitting) begin
            tbyte = plain_text[byteN*8 +: 8];
            begin_transmit = 1'b1;
            byteN = byteN + 1;
            if(byteN == 8) begin
                state = IDLE;
            end
        end
    end
    endcase
end

endmodule

Segunda versión "de trabajo":

module transmission_test(sysclk, rxd, txd, LED, button);


input sysclk, rxd, button;

output txd;

output reg LED;


wire receiving_complete, isReceiving, isTransmitting, isError;

reg begin_transmit;

reg [7:0] tbyte;


wire [7:0] rbyte;


reg [2:0] state;

reg [63:0] plain_text;

integer bytes_remaining;


parameter IDLE = 0, BEGIN_TRANSMISSION = 1, UPDATE_DATA = 2, SEND_BYTES = 3, DONE = 4;


uart uart1(
.clk(sysclk),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_receiving(isReceiving), 
.is_transmitting(isTransmitting),
.recv_error(isError)
);


always @(posedge sysclk)
begin
    begin_transmit = 1'b0;
    case(state)
    IDLE: begin
        if(button==1'b0) begin
            LED = 1'b1;
            plain_text = 64'hDEADBEEFDEADBEEF;
            state = BEGIN_TRANSMISSION;
        end else begin
            LED <= 1'b0;
        end
    end
    BEGIN_TRANSMISSION: begin
        tbyte = plain_text[7:0];
        begin_transmit = 1'b1;
        bytes_remaining = 7;
        state = UPDATE_DATA;
    end
    UPDATE_DATA: begin
        plain_text = plain_text >> 8;
        state = SEND_BYTES;
    end
    SEND_BYTES: begin
        if(!isTransmitting) begin
            tbyte = plain_text[7:0];
            begin_transmit = 1'b1;
            bytes_remaining = bytes_remaining - 1;
            if(bytes_remaining == 0) begin
                state = IDLE;
            end else begin
                state = UPDATE_DATA;
            end
        end
    end
    endcase
end

endmodule

REVISIÓN:

Siguiendo el consejo de @dwikle, en lugar de usar un estado "catch-all" que recorre el envío de los 8 bytes, creé un estado separado para cada byte que debía enviarse.

Aquí está el código:

module transmission_test_3(sysclk, rxd, txd, LED, button);

input sysclk, rxd, button;
output txd;
output reg LED;

wire receiving_complete, isReceiving, isTransmitting, isError, reset;
reg begin_transmit;
reg [7:0] tbyte;

wire [7:0] rbyte;

reg [2:0] state = 4'b0;
reg [63:0] plain_text = 64'h0;

uart uart1(
.clk(sysclk),
.rst(reset),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_receiving(isReceiving), 
.is_transmitting(isTransmitting),
.recv_error(isError)
);

always @(posedge sysclk)
begin
    begin_transmit = 1'b0;
    case(state)
    4'b0000: begin
        if(button==1'b0) begin
            LED = 1'b1;
            plain_text = 64'hDEADBEEFAAABACAD;
            state = 4'b0001;
        end else begin
            LED = 1'b0;
        end
    end
    4'b0001: begin
        tbyte = plain_text[7:0];
        begin_transmit = 1'b1;
        state = 4'b0010;
    end
    4'b0010: begin
        if(!isTransmitting) begin
            tbyte = plain_text[15:8];
            begin_transmit = 1'b1;
            state = 4'b0011;
        end
    end
    4'b0011: begin
            if(!isTransmitting) begin
                tbyte = plain_text[23:16];
                begin_transmit = 1'b1;
                state = 4'b0100;
            end
        end
    4'b0100: begin
        if(!isTransmitting) begin
            tbyte = plain_text[31:24];
            begin_transmit = 1'b1;
            state = 4'b0101;
        end
    end
    4'b0101: begin
        if(!isTransmitting) begin
            tbyte = plain_text[39:32];
            begin_transmit = 1'b1;
            state = 4'b0110;
        end
    end
    4'b0110: begin
        if(!isTransmitting) begin
            tbyte = plain_text[47:40];
            begin_transmit = 1'b1;
            state = 4'b0111;
        end
    end
    4'b0111: begin
        if(!isTransmitting) begin
            tbyte = plain_text[55:48];
            begin_transmit = 1'b1;
            state = 4'b1000;
        end
    end
    4'b1000: begin
        if(!isTransmitting) begin
            tbyte = plain_text[63:56];
            begin_transmit = 1'b1;
            state = 4'b0000;
        end
    end
    endcase
end
endmodule

Sin embargo, los resultados siguen siendo los mismos: recibo cada dos bytes. ¿Lo que da?

¿Algunas ideas?

PD: publiqué el código UART en mis documentos de Google, si necesita echar un vistazo. :)

Módulo UART

ACTUALIZAR:

La pregunta original de este hilo provino de intentar aislar un problema de comunicación en un módulo más grande.

El proyecto simplemente acepta 64 bits (8 bytes) de datos de la PC, encripta los datos con el algoritmo DES, y envía el mensaje encriptado (64 bits, nuevamente).

La transferencia funciona, pero (lo que parece ser arbitrario) cuelga. Cuando intento hacer 1000 encriptaciones, en promedio puedo manejar alrededor de 250, a veces haciendo las 1000 con éxito y, a veces, tal vez solo haciendo 20 o 50.

Pensé que la transferencia era un cuello de botella en el lado de la transmisión de la comunicación, que era que estaba reescribiendo la transmisión y, por lo tanto, el propósito de este hilo. Sin embargo, me he dado cuenta de que el problema en realidad radica en el lado de la recepción de la transferencia. Lo que parece estar sucediendo es que después de realizar varias ejecuciones exitosas, la máquina de estado recopila 7 bytes nuevamente y luego, de alguna manera, pierde el último byte del fragmento de 64 bits y se atasca en el estado buscando el último byte.

Intenté simular el diseño, pero siento que el estímulo que estoy proporcionando no es del todo kosher. El banco de pruebas debe enviar 8 bytes de datos a través del uart, y luego la máquina de estado debe continuar, encriptando y devolviendo el mensaje.

Sin embargo, proporcionar diferentes vectores de datos a la línea rx del uart da resultados drásticamente diferentes. Dependiendo de los datos que estoy usando, el uart a veces recibe solo 7 bytes (que es el problema que estoy viendo con el hardware real) o incluso devuelve errores.

Entonces, los problemas que estoy tratando de resolver son:

1) Puedo recibir y transmitir con éxito varias decenas a cientos de cifrados, pero la comunicación se cuelga en puntos arbitrarios, y parece ser que cuando esto sucede, la máquina de estado ha recopilado 7 bytes y está buscando el último.

2) Para diagnosticar este problema, intenté investigar los resultados de la simulación. Sin embargo, incluso estos parecen dar un comportamiento inesperado, y me temo que tal vez estoy dando estímulos incorrectamente.

Cualquier comentario o sugerencia para la implementación del banco de pruebas, o lo que puede estar causando el cese de la comunicación, sería muy apreciado. Si estas preguntas parecen elementales, pido disculpas, todavía estoy aprendiendo.

Adjunté un zip en mis documentos de Google con todos los archivos relevantes, incluido el banco de pruebas.

https://docs.google.com/open?id=0B4WyEjzmIhtNN0V6a0x5U19SMUU

También publicaré el módulo de nivel superior aquí como referencia.

module rs232_neek(sysclk, rxd, txd, reset, LED);

input sysclk, rxd, reset;
output txd;
wire receiving_complete, isTransmitting, isReceiving, isError;

output reg [3:0] LED;   //The LEDs are used simply as debug - to determine which state the machine gets held-up in.

reg begin_transmit;
reg [7:0] tbyte;

wire [7:0] rbyte;

parameter FIRST_BYTE = 0, GET_BYTES = 1, BEGIN_ENC = 2, CHECK_ENC_STATUS = 3,     BEGIN_TRANSMISSION = 4, SEND_BYTES = 5;

reg [2:0] state = 3'b0;
integer byteN = 0;

reg [3:0] sel = 4'b0;
reg [63:0] plain_text;
reg [63:0] cipher_text;
wire [63:0] cipher_net;

uart uart1(
.clk(sysclk),
 .rst(~reset),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_transmitting(isTransmitting),
.is_receiving(isReceiving),
.recv_error(isError)
);

des des1(
 .clk(sysclk),
 .key(56'h0),
 .roundSel(sel),
 .decrypt(1'b0),
 .desIn(plain_text),
 .desOut(cipher_net)
 );

always @(posedge sysclk)
begin

    if(~reset) begin
        state = FIRST_BYTE;
    end

    LED = 4'b1111;

    case(state)
        FIRST_BYTE: begin
            LED[0] = 1'b0;
            begin_transmit = 1'b0;
            if(receiving_complete) begin
               plain_text[7:0] = rbyte;
                byteN = 1;
                state = GET_BYTES;
            end
        end
        GET_BYTES: begin
            LED[1] = 1'b0;
            if(receiving_complete) begin
                plain_text[byteN*8 +: 8] = rbyte;
                byteN = byteN + 1;
                if(byteN == 8) begin
                    state = BEGIN_ENC;
                end
            end
        end
        BEGIN_ENC: begin
            sel = 4'b0;
            state = CHECK_ENC_STATUS;
        end
        CHECK_ENC_STATUS: begin
            LED[2] = 1'b0;
            sel = sel + 1;
            if(sel == 15) begin
                state = BEGIN_TRANSMISSION;
            end
        end
        BEGIN_TRANSMISSION: begin
            cipher_text = cipher_net;
            tbyte = cipher_text[7:0];
            begin_transmit = 1'b1;
            byteN = 1;
            state = SEND_BYTES;
        end
        SEND_BYTES: begin
            LED[3] = 1'b0;
            if(!isTransmitting && !begin_transmit) begin
                tbyte = cipher_text[byteN*8 +: 8];
                begin_transmit = 1'b1;
                byteN = byteN + 1;
                if(byteN == 8) begin
                    state = FIRST_BYTE;
                end
            end else begin
                begin_transmit = 1'b0;
            end
        end
    endcase
end

endmodule
Para ver qué pasa exactamente, creo que sería buena idea usar 8 códigos diferentes. Si ahora recibe 0xBE no sabe si es el primero o el segundo.
Francamente, no estoy seguro de cómo funciona ninguno de ellos. ¿Se supone que "begin_transmit" pulsa por cada byte enviado? Veo dónde lo está configurando, pero no dónde se borra.
Sí, se supone que pulsa por cada byte. Estoy intentando borrar la bandera al comienzo del bloque siempre.
También intenté usar diferentes vectores como entrada, solo para estar seguro. El mismo problema.

Respuestas (2)

El problema es que su código "transmission_test" está escrito de tal manera que asume que "is_transmitting" del UART se cumple antes del borde del reloj que sigue a la afirmación de "begin_transmit". No es así: se necesita un ciclo de reloj antes de que el transmisor UART salga de su estado "inactivo", después de lo cual "is_transmitting" es verdadero.

Como resultado, su máquina de estado "transmission_test" avanza dos estados por cada byte transmitido, y solo ve uno de cada dos bytes en la salida del UART.

Hay muchos problemas menores con sus ejemplos de código, pero lo principal que debe solucionar es verificar que "is_transmitting" se haya cumplido antes de avanzar al siguiente byte en el mensaje.

Esto habría sido bastante obvio si se hubiera tomado el tiempo de simular este proyecto. No puedo dejar de enfatizar la importancia de la simulación en la verificación del código FPGA. Tómese el tiempo para familiarizarse con su simulador y aprender a escribir buenos bancos de pruebas de módulos.

Dave: muchas gracias por la respuesta y por tomarse el tiempo para investigar el problema. Obviamente, debería haber analizado los resultados de la simulación más de cerca. Todavía estoy aprendiendo la mayoría de los matices de Verilog y asumí que estaba pasando por alto algo en el código mismo. Mencionaste que hay varios otros problemas en el código. Me encantaría que pudieras arrojar algo de luz sobre eso. Como dije, todavía estoy aprendiendo a escribir buen HDL y agradezco cualquier comentario o crítica. ¡Gracias!
@KristinBarber: Bueno, solo mirando 'transmission_test_3.v', no hay asignación a la señal de "reinicio". No estoy seguro de cuál era su intención con la señal del "botón", pero por la forma en que está configurado, el circuito transmitirá continuamente mientras esté activo (bajo), en lugar de enviar un mensaje cada vez que presione un botón. Y supongo que es más un problema de estilo que un error, pero realmente no me gusta la "asignación predeterminada" para "comenzar a transmitir" fuera de la declaración del caso. Me perdí esto la primera vez, y este tipo de cosas hace que su código sea difícil de leer en general.
@KristinBarber: El código uart.v en sí tiene algunos problemas más serios, aunque parece que lo obtuviste de otra persona. El bloque "if (rst) begin..." es completamente ineficaz, ya que es anulado por el resto del código dentro del bloque always -- todo ese otro código debe estar dentro de un "else" asociado con el "if (primero)". Y hay muchos estados internos que no están configurados en valores conocidos por el reinicio de todos modos. Puede que al final no importe realmente, pero si es así, debe documentarse claramente como tal.
Dave - Gracias por todos los aportes. Realmente me vendría bien un poco de ayuda para diagnosticar un problema mayor en cuestión, que describí en una actualización de la publicación de la pregunta original. Si pudieras echarle un vistazo me ayudaría muchísimo.
@kbarber: el problema con su banco de pruebas es que solo envía un bit de inicio al comienzo del mensaje, en lugar de un bit de inicio por byte. Esto provoca un comportamiento dependiente de los datos porque el UART interpreta el primer cero de los datos como un bit de inicio en lugar de un bit de datos. Todavía estoy investigando posibles problemas con el hardware real.
Dave - Vaya, qué error tan tonto. Es curioso cómo después de mirar el código varias veces, puedes pasar por alto por completo lo obvio. ¡Gracias de nuevo! Si puede encontrar alguna explicación de por qué el hardware real se está comportando mal, me encantaría escucharlo, ¡ha sido la raíz de mi problema durante días! Es desconcertante por qué en puntos arbitrarios, con datos arbitrarios, algunos de los datos se pierden.
@kbarber: El único problema potencial que veo está en el módulo UART. Cuando detecta un "inicio falso" en el lado de recepción, se bloquea por dos bits, lo que significa que podría perder un bit de inicio legítimo en ese tiempo. Si obtiene inicios falsos con la configuración de su hardware, junto con algunos bytes de datos que son en su mayoría (0xFF, 0xFE, etc.), podría terminar perdiendo un byte completo de un grupo de ocho. Realmente no hay razón para ir al estado RX_ERROR en un inicio falso, simplemente debe volver a RX_IDLE y comenzar a buscar el siguiente bit de inicio de inmediato.
Gracias, intentaré hacer estos cambios y probaré.
Desafortunadamente, esto no pareció ayudar. También intenté usar un ejemplo de UART proporcionado por Altera de su libro de cocina de manera similar, en caso de que hubiera una pequeña discrepancia con el código principal que estaba usando. Aún así, el problema persiste. ¿Crees que existe la posibilidad de que el problema resida en el adaptador USB a serie?
Posiblemente. Es hora de probar algunos loopbacks. Primero, simplemente conecte los pines 2 y 3 juntos en el adaptador y vea si pasa los datos de manera confiable. También puede hacer esto dentro de la FPGA, en el lado serial de la UART. Luego, intente hacer un bucle de los datos directamente en la interfaz paralela de la UART, dentro de la FPGA. Si todo eso funciona de manera confiable, entonces el problema está en otra parte dentro del FPGA. De lo contrario, ahora sabe dónde centrar su atención.

Sin conocer los requisitos de la UART, supongo que el problema radica en begin_transmit. En su caso de "trabajo" begin_transmit, se borrará para cada byte ya que se está moviendo a través del estado UPDATE_DATA. Sin embargo, en el caso de que no funcione, permanecerá en el estado SEND_BYTESdurante toda la transmisión y begin_transmitsolo se borrará si isTransmitting == 1. (Tenga en cuenta que begin_transmitse asigna 0antes de la declaración del caso, lo que crea una asignación predeterminada).

Simulé el caso que no funciona y begin_transmitsolo pulsa cada dos bytes, lo que explicaría lo que está viendo si el UART espera ver un posedge en begin_transmit. Sin el modelo UART, modelé isTransmittingasí para la simulación.

always @(posedge sysclk) begin
  isTransmitting <= begin_transmit;
end
Oh, eso es esclarecedor, en realidad. Tenía la impresión de que cada vez que se ingresaba el bloque siempre, se evaluarían las declaraciones y ENTONCES se evaluaría el caso. Sin embargo, está diciendo que en una situación como esta, ¿algo antes de que una declaración de caso en un bloque siempre se trate como un caso predeterminado?
He actualizado mi publicación original para acomodar estas sugerencias. Sin embargo, sigo teniendo problemas. Eche un vistazo y avíseme si tiene más sugerencias, si es posible.
Con respecto al "predeterminado", me parece que dijo que si no se ejecuta ninguno de los casos, o si una prueba if dentro del caso seleccionado hace que se omita el resto del código, entonces la variable mantendrá el valor que tenía antes, por lo tanto, conserva el que tenía en la parte superior del bloque y, por lo tanto, ese es esencialmente un valor predeterminado. Predeterminado, como en "este será el valor a menos que algo más lo cambie".