Diseño de UART Verilog Vivado: problemas de implementación / flujo de bits

-1

Actualmente estoy trabajando en mi primer proyecto Verilog para mi empresa.
Soy un ingeniero recién nombrado, por lo que mi experiencia es simplemente académica.
Perdóname si mi inglés no es perfecto, intentaré hacerlo lo mejor posible.

Necesito implementar una comunicación UART en un Xilinx Virtex-7 FPGA. En este momento, he trabajado tanto en el receptor como en el transmisor Y se han conectado entre sí generando una comunicación de bucle invertido. Como tuvimos un error de 1 bit cada 50 o más, me pidieron que implementara un control de suma de comprobación dentro de mi unidad.

Anteriormente tuve problemas con el bloqueo de las asignaciones sin bloqueo, resuelto gracias a la increíble comunidad StackOverflow.

En este momento, cuando intento enviar mi paquete (10 bytes + una suma de comprobación, que calculo previamente), mi unidad parece recibir el contenido correctamente (vinculé RX_states a GPIO Leds para verificar si había actividad), cuando Envío la suma de comprobación que activa y devuelvo ... una cadena completamente incorrecta, excepto exactamente el primer carácter.

Me gustaría pegar el código completo aquí, pero supera el espacio máximo permitido, así que truncaré donde el código se haya depurado lo mejor posible.

En particular, en este momento siento que el problema reside en la cadena de buffers de Receiver-checksum

module async_receiver(
    input reset,
    input clk,
    input RxD, 
    input Rx_enable, //buf_available from the buffer
    output wire RxD_ready, //data_trigger for the buffer
    output wire [7:0] RxD_data
    //output wire [2:0] Rx_state_scope // data received, valid only (for one clock cycle) when RxD_ready is asserted   
    );
    //functional parameters
    parameter ClkFrequency = 200000000; // 200MHz
    parameter Baud = 9600;
    parameter Oversampling = 16;

    reg RxD_ready_internal;

    assign RxD_ready = RxD_ready_internal;


    //state encoding
    localparam  
    RX_IDLE = 3'b000, 
    RX_START = 3'b001,  
    RX_BITS_1 = 3'b010,
    RX_BITS_2 = 3'b011,
    RX_BITS_3 = 3'b100,
    RX_BITS_4 = 3'b101,
    RX_STOP = 3'b110,
    RX_INIT = 3'b111;

    //support variables
    reg [7:0]  RxD_data_int;  
    reg [2:0] Rx_state; //3 bits for 6 states
    integer cnt_0, cnt_1; //input values for my variables
    integer bit_counter;   //bit counter variable
    integer delay_cnt;
    reg RxD_int;
    integer i; //vector index for output data

    //trigger recognition
    wire START_Trigger;
    StartDetectionUnit SDU ( .clk(clk), .state (Rx_state), .signal_in (RxD), .trigger (START_Trigger));

    //tick gen
    wire OversampledTick;        
    BaudTickGenOvers #(ClkFrequency, Baud, Oversampling) as (.reset(reset), .clk(clk), .trigger(START_Trigger), .tick(OversampledTick)); //generates Bitticks for recognition

    //wire assignments     
  //  assign Rx_state_scope = Rx_state;
    assign RxD_data = RxD_data_int;



    always @(posedge clk) begin


        if (reset) 
            begin
                 RxD_ready_internal <= 0;
                 RxD_int <= 1'b0;
                 RxD_data_int <= 8'b00000000;
                 Rx_state <= RX_IDLE;

            end
            else if (Rx_enable)
                        case (Rx_state)


                        RX_INIT:
                                begin
                                    i <= 0;
                                    delay_cnt <= 0;
                                    bit_counter <= 0;
                                    cnt_0 <= 0;
                                    cnt_1 <= 0;
                                    Rx_state <= RX_IDLE;

                                end


                        RX_IDLE: 
                                begin
                                    //reinitialization after each cycle
                                    RxD_ready_internal <= 0;
                                    RxD_int <= 1'b0;
                                    i <= 0;
                                    delay_cnt <= 0;
                                    bit_counter <= 0;
                                    cnt_0 <= 0;
                                    cnt_1 <= 0;

                                        if (START_Trigger) Rx_state <= RX_START; //when RxD is received as zero during an oversampling tick, it starts the state machine

                                end                                                              


                        RX_START:
                                begin

                                if (OversampledTick)

                                   begin
                                            //initialize the length of the word
                                            bit_counter <= 7;
                                            if (delay_cnt > 17 ) //wait for a bit period + an initial delay to avoid sampling on the edge of the bit --> 16 + 2 ticks 
                                                begin
                                                    delay_cnt <= 0;
                                                    Rx_state <= RX_BITS_1;
                                                end
                                            //if 16 ticks have not passed, it increments the counter and stays in the same state
                                            else begin 
                                                 delay_cnt <= delay_cnt+1;
                                                 Rx_state <= RX_START;
                                                 end
                                   end
                                end     


                        RX_BITS_1:
                                    begin

                                        if (OversampledTick) 
                                        begin

                                            if (delay_cnt < 1 ) //i want to execute the recognition ONLY at soon as I change state not for every wait tick
                                                begin
                                                    RxD_int <= RxD; //copy the received bit into an internal "buffer"
                                                    if (RxD_int == 1) cnt_1 <= cnt_1+1;
                                                    else if (RxD_int == 0) cnt_0 <= cnt_0 +1;
                                                end           
                                            if (delay_cnt > 2) //4 ticks delay
                                            begin
                                                delay_cnt <= 0;
                                                Rx_state <= RX_BITS_2; //wait for a few "baud ticks" so the samples are equally interspaced
                                            end
                                            else begin 
                                                     delay_cnt <= delay_cnt+1; //if the wait time has not been completed it stays in the same state
                                                     Rx_state <= RX_BITS_1;
                                                 end
                                            end
                                        end


                        RX_BITS_2:
                                begin
                                if (OversampledTick) //I think this is the most important thing to verify about actual functionality     
                                    begin
                                        if (delay_cnt < 1) //i want to execute the recognition ONLY at soon as I change state not for every wait tick
                                            begin
                                                RxD_int <= RxD; //copy the received bit into an internal "buffer"
                                   //        $display ("seconda acquisizione avviene al tempo", $time);
                                                if (RxD_int == 1) cnt_1 <= cnt_1+1;
                                                else if (RxD_int == 0) cnt_0 <= cnt_0 +1;
                                            end                
                                        if (delay_cnt > 2) //4 ticks delay
                                        begin
                                            delay_cnt <= 0;
                                            Rx_state <= RX_BITS_3; //wait for a whole "baud cycle"
                                        end
                                        else begin
                                             delay_cnt <= delay_cnt+1;
                                             Rx_state <= RX_BITS_2;
                                             end
                                    end
                                end

                        RX_BITS_3:

                                begin
                                    if (OversampledTick) //I think this is the most important thing to verify about actual functionality     
                                    begin
                                        if (delay_cnt < 1)
                                            begin
                                                RxD_int <= RxD;
                                    //          $display ("terza acquisizione avviene al tempo", $time);
                                                if (RxD_int == 1) cnt_1 <= cnt_1+1;
                                                else if (RxD_int == 0) cnt_0 <= cnt_0 +1;
                                            end                 
                                        if (delay_cnt > 2) //4 ticks delay
                                        begin
                                            delay_cnt <= 0;
                                            Rx_state <= RX_BITS_4; //wait for a whole "baud cycle"
                                        end
                                        else begin
                                             delay_cnt <= delay_cnt+1;
                                             Rx_state <= RX_BITS_3; 
                                             end                       
                                    end
                                end

                        RX_BITS_4:
                                begin
                                if (OversampledTick) //I think this is the most important thing to verify about actual functionality     
                                    begin
                                    if (delay_cnt < 1)
                                        begin
                                            RxD_int <= RxD;
                               //             $display ("quarta acquisizione avviene al tempo", $time);                                  
                                            if (RxD_int == 1) cnt_1 <= cnt_1+1;
                                            else if (RxD_int == 0) cnt_0 <= cnt_0 +1;
                                        end                
                                    if (delay_cnt > 2) //4 ticks delay - one tick dedicated to writing
                                    begin

                                        //WRITE FUNCTION
                                        //after checking whether there were 1s or 0s recognized, I write the appropriate value
                                        //and increment the output index
                                        if (cnt_1 >= 3) 
                                            begin
                                                RxD_data_int[i] <= 1;
                               //                 $display ("OUTPUT_UART bit %b all'indice numero %d al tempo: ", RxD_data[i], i, $time);
                                                i<=i+1;
                                            end

                                        else if (cnt_0 >=3)
                                            begin
                                                RxD_data_int[i] <= 0;
                               //                 $display ("OUTPUT_UART bit %b all'indice numero %d al tempo: ", RxD_data[i], i, $time);
                                                i<=i+1; 
                                            end                                

                                        else begin //error case when neither a 0 nor a 1 is recognized
                                         //   $display ("Frame Error!"); 
                                            Rx_state <= RX_IDLE;
                                                 end   

                                         //END OF THE BYTE DESCRIPTION
                                        if (bit_counter == 0) begin 
                                           delay_cnt <= 0;
                                 //          $display ("Detecto la fine del byte al tempo", $time);
                                           Rx_state <= RX_STOP; 
                                           end   
                                           else begin //modify the RX_BITS variables here instead of an in an iterative state
                                                delay_cnt <= 0;
                                                cnt_0 <= 0;
                                                cnt_1 <= 0;
                                                bit_counter <= bit_counter -1;
                                                Rx_state <= RX_BITS_1;    //i have waited for the correct amount of time but the bit wasn't the last one                              
                                                end

                                    end

                                    else begin //wait case when I have not reached the end of the 16 ticks
                                             delay_cnt <= delay_cnt+1; //delay count
                                           //  $display ("valore del delay_cnt %d al tempo:", delay_cnt, $time);
                                             Rx_state <= RX_BITS_4;   
                                         end 


                                end
                            end

                        RX_STOP: 
                                begin

                                if (OversampledTick) 
                                    begin
                                            RxD_ready_internal <= 1;
                                           // $display ("RX RECEIVED: %b aka %c @t=", RxD_data_int, RxD_data_int, $time);
                                            Rx_state <= RX_IDLE; //wait for a whole "baud cycle"
                                        end

                        end                


                        default: Rx_state <= RX_IDLE;
                        endcase
                    end


endmodule   

module checksum256 (
    input reset,
    input [7:0]data, //RxD_data is the data necessary for checksum
    input enable, //RxD_ready flag enables the checksum
    output reg sum_ok //flag to signal that my operation went correctly [to the control unit and to the transmission buffer unit]
    );

    //21.09 SUM_RESULT_INTERNAL becomes SUM_OK directly

    reg [7:0]sum_value; //counting the sum
    integer m; // count the # of byte till i am inside the control byte    
    reg sum_ok_internal;

   // assign sum_ok = sum_ok_internal;    

always @ (posedge enable or posedge reset) //triggers whenever enable (RxD_ready)signal has a rising edge
       begin
        if (reset) 
            begin
                sum_value = 8'b00000000;
                m = 0;
                sum_ok = 0;
            end
           //11th byte is my control byte
           else if (enable)
                begin if (m == 10) 
                    begin
                    //ROUTINE DI CONFRONTO DELLA SOMMA
                        if (sum_value == data) sum_ok = 1;
                        else sum_ok = 0;
                      //  $display ("checksum_result vale %b al tempo: ", sum_ok, $time);
                            sum_value = 0; //reset the sum value
                            m = 0; //reset the word counter    
                    end
                   else begin
                            sum_value = sum_value + data;
                           // $display ("sum_value vale %b al tempo:", sum_value, $time);
                            m = m + 1;
                            sum_ok =0;

                   end 
              end     
       end

endmodule

module output_Dbuffer (
    input reset,
    input [7:0]input_data, //data coming from the receiver
    input data_trigger, //RxD_ready flag tells me that I can copy the data
    input checksum_trigger, //SUM_OK flag from Checksum unit is the trigger for the output buffer
    input RTS_n, //TX_BUSY flag of the transmitter is my ready to send flag
    input clk, //ck needed for the FSM
    output wire TX_trigger, //TX_START flag of the transmitter now comes from THIS unit instead of Receiver
    output wire [7:0]TX_data, //byte for transmission
    output wire buf_available ,

    output wire [2:0]buf_state_scope
    );

    //internal variables
    reg [7:0] mem[0:9]; //memory init, 10 * 8 bit locations
    integer m, n, i, j ; //M = row [a.k.a. bytes], N = column [a.k.a. single bits]

    reg TX_trigger_int;
    reg [7:0] TX_data_int; 
    reg buf_available_flag;
    reg sum256_ok,  checksum_sent;        
    reg [7:0]checksum_buff ;

    //buffer FSM required variables 
    localparam  //state enumeration declaration
        BUF_IDLE = 3'b000, 
        BUF_START = 3'b001,
        BUF_BYTES = 3'b010,  
        BUF_BUSY = 3'b011,
        BUF_TX_CHECKSUM = 3'b100,
        BUF_INIT = 3'b101;

    reg [2:0] buf_state; //2 bits for 4 states

    //static assignments of OUTPUTS : Transmission Flag and Transmission Data (content)
    assign TX_trigger = TX_trigger_int;
    assign TX_data = TX_data_int;

    //signal for sending outside of the system my buf state bits
    assign buf_state_scope = buf_state;
    //flag to signal if the buffer is available or busy
    assign buf_available = buf_available_flag;


  //Block for copying my input data inside the memory    
    always @(data_trigger or reset)
                begin
                  if (reset)   
                    begin
                        for (n = 0; n < 10; n = n+1) mem [n]= 8'h00;
                        i=0;
                        checksum_buff = 8'h00;
                    end     
                   else if (data_trigger)
                        begin 
                            if ( i < 10) 
                            begin
                                mem[i] = input_data;
                              //  $display ("mem[%d ] vale %b al tempo:", i, mem[i],$time);
                                i = i + 1; //the increment happens at the end of current iteration so it doesn't re-iterate on itself
                            end
                        else 
                            begin
                                checksum_buff = input_data; //copy the checksum inside my buffer
                              //  $display("checksum_buff vale %b al tempo:", checksum_buff, $time);
                                i = 0; //the increment happens at the end of current iteration so it doesn't re-iterate on itself

                                 // for (n = 0; n < 10; n = n+1)
                                // $display ("mem[%d] = %b aka %c", n, mem[n], mem[n]);

                                // $display ("checksum = %b aka %c", checksum_buff, checksum_buff);
                            end
                       end
                end


//Block for continuously checking the checksum flag                                   
   always @(*)
    begin
         sum256_ok =0;
         if (reset) sum256_ok = 0;
         else if (checksum_trigger == 1) sum256_ok = 1;        
         else if (checksum_sent == 1) sum256_ok = 0;

    end    


    //Block for transmitting [here I manage the TX_Data and TX_Trigger functionality]
always @(posedge clk)
        begin
            if (reset)
                begin
                        buf_state <= BUF_INIT;
                        TX_trigger_int <= 0;
                        TX_data_int <= 8'b00000000;
                        buf_available_flag <= 1;        
                end

            else case (buf_state)

                BUF_INIT: 
                    begin
                            buf_state <= BUF_IDLE;
                            checksum_sent <=0;
                            j = 0;
                    end


                BUF_IDLE:
                    begin
                        TX_trigger_int <= 0;
                        TX_data_int <= 8'b00000000;
                        m =0;
                        n =0;
                        j =0; 
                        checksum_sent <=0;
                        if (sum256_ok) 
                            begin
                                //check if the TX is not busy
                                if (RTS_n == 0) buf_state <= BUF_START;
                            end


                    end

                BUF_START:
                        begin
                            TX_trigger_int <=0;

                           // if ((i == 0) || ( (j - i) > 1 ))

                            buf_state <= BUF_BYTES;
                               //     else begin
                                      //  $display ("BUFFER BUSY @time:", $time);
                                //        buf_available_flag <= 0;
                               //         buf_state <= BUF_BUSY;   
                               //     end 
                        end   

                BUF_BYTES:
                            begin

                                //check if the TX is busy
                                if (RTS_n==0) 
                                    begin

                                        if (j > 9) 
                                                begin
                                                    TX_trigger_int <= 0;
                                                    buf_state <= BUF_TX_CHECKSUM;
                                                end
                                        else begin
                                                TX_data_int <= mem[j];
                                             //   $display ("BUFFER: %b -> TX", mem[j]);
                                                TX_trigger_int <= 1;
                                                j <= j+1;
                                                buf_state <= BUF_START;
                                            end
                                    end

                            end

                BUF_BUSY:
                                begin
                                    if (RTS_n == 0)
                                        begin 
                                           // $display ("BUFFER AVAILABLE AGAIN @time:", $time);
                                            buf_state <= BUF_START;
                                            buf_available_flag <= 1;
                                        end

                                end

                BUF_TX_CHECKSUM:
                                begin

                                    if (RTS_n==0) begin
                                            TX_data_int <= checksum_buff;
                                            checksum_sent <= 1;
                                            TX_trigger_int <= 1;
                                            buf_state <= BUF_IDLE;

                                        end
                                end

                        default: buf_state = BUF_IDLE;

                        endcase


        end




endmodule
    
pregunta Michele Marconi

2 respuestas

0

Como dije en mi comentario, mem es un lating sensible al nivel inferido (para el resto de la respuesta usaré latch para todo lo que sea sensible al nivel, y flop para cualquier sensible al borde). Los bloqueos se deducen en bloques que no son activados por borde ( posedge / negedge ) y no se asignan a un valor determinístico si todas las ramas lógicas.

Aquí está su lógica original reformateada con mis comentarios:

always @(data_trigger or reset) // <-- poorly inferred latch
begin
    if (reset) // Asynchronous reset because 'reset' is in the sensitivity list
    begin
        for (n = 0; n < 10; n = n+1) mem[n] = 8'h00;
        i = 0;
        checksum_buff = 8'h00;
    end     
    else if (data_trigger) // <-- Also asynchronous
    begin 
        if ( i < 10) 
        begin
            mem[i] = input_data;
            i = i + 1; // <-- as a latch will cause feedback loop on FPGA, will not be detected on in Simulations
        end
        else 
        begin
            checksum_buff = input_data;
            i = 0; // <-- also will cause a feedback loop to 'if(i<10)' in FPGA
        end
    end
end 

Excepto por la lógica síncrona, los sintetizadores no miran la lista de sensibilidad. Por lo tanto, input_data y i son en realidad parte de la lista de sensibilidad durante la síntesis. La simulación utiliza la lista de sensibilidad, por lo tanto, ignorará los cambios a input_data y i . Este es un lugar donde la simulación y la síntesis no coincidirán.

i es parte de la sensibilidad real y se actualiza en cada paso del bloque siempre. Esto crea una retroalimentación asíncrona que data_trigger es alto y reset es bajo. Si tuviera que agregar i a su lista de sensibilidad, su simulación probablemente se cuelgue de un bucle infinito. En FPGA hay un ligero retraso en la propagación, por lo que tendrá resultados de datos aparentemente aleatorios. Si estuviera midiendo potencia, también vería un alto consumo de energía en esta condición.

Si revisa sus archivos de registro, puede haber una advertencia acerca de los comentarios asíncronos.

i necesita ser un flop. mem y checksum_buff también deberían ser flops. A partir de una revisión de su código, parece que todo se está ejecutando en el dominio posedge clk . Por lo tanto, te sugiero que cambies el código a:

always @(posedge clk)
begin
    if (reset)   
    begin
        for (n = 0; n < 10; n = n+1) mem[n] <= 8'h00;
        i <= 0;
        checksum_buff <= 8'h00;
    end     
    else if (data_trigger)
    begin 
        if ( i < 10) 
        begin
            mem[i] <= input_data;
            i <= i + 1;
        end
        else 
        begin
            checksum_buff <= input_data;
            i <= 0;
        end
    end
end

Con este reinicio también será síncrono. Si realmente desea un reinicio asíncrono, cámbielo a always @(posedge clk or posedge reset) . Si realmente desea que data_trigger sea el evento de reloj (no lo recomiendo), cambie el posedge clk a posedge data_trigger AND reemplace else if (data_trigger) con %código%. Cualquier señal else / posedge a la que se hace referencia en el bloque se trata como un disparador asíncrono. Tiene este problema en su módulo negedge .

La lógica combinacional de Verilog debe escribirse en checksum256 o en el sinónimo always @* . Se encontrará con el código que proporciona una lista de señales en lugar de un literal always @(*) ; no hagas eso Ese estilo es para el legado Verilog-1995. Dado que Verilog-2001 * / @* es la estrategia preferida para limitar el riesgo en una lista de sensibilidad incompleta. La otra cosa para una lógica combinatoria adecuada es que cada señal del lado izquierdo (LHS) debe asignarse a un valor determinado para todas las posibles ramas lógicas. Asignarlo a sí mismo no cuenta. Asignándole un valor determinado en la parte superior de un bloque siempre antes de que un @(*) o if le asigne una asignación predeterminada. La lógica combinacional debe asignarse con instrucciones de bloqueo ( case ).

La lógica síncrona debe tener un disparador de sincronización = o posedge . No haga referencia a la señal de reloj en el cuerpo del bloque siempre. Puede haber hasta dos señales negedge o posedge adicionales en la lista de sensibilidad para reinicio / preset asíncrono, y asíncrono debe asignar registros a constantes. No todos los FPGAs soportan flops con reset / preset asíncrono; algunos tienen un número limitado. Consulta el manual de tu tablero. La lógica síncrona debe asignarse con instrucciones no bloqueantes ( negedge ).

Por lo general, deben evitarse los cierres. Desafortunadamente, se pueden inferir involuntariamente con lo que debería ser una lógica combinatoria. Un bloqueo ocurre cuando no todas las ramas lógicas asignan la señal a un valor determinista. Si necesita un pestillo, mantenga la lógica simple y asigne sin bloqueo.

always @* begin
  if (enable) Q <= D;
end

Si tiene SystemVerilog, puede ser más explícito reemplazando <= con always @* y always_comb . De esta manera, su sintetizador puede hacer un mejor trabajo advirtiéndole sobre los cierres involuntarios.

Para su información: Verilog no bloqueante se inspiró / copió de VHDL. El uso de bloqueo y no bloqueo debe ser similar entre los dos.

    
respondido por el Greg
0

Edité mi bloque de comprobación a lo siguiente:

always @ (posedge clk)
       begin
        if (reset) 
            begin
                sum_value[7:0] <= 8'b00000000;
                m <= 0;
                sum_ok <= 0;
            end
            else if (enable)
                begin if (m == 10) 
                    begin
                        if (sum_value[7:0] == data[7:0]) sum_ok <= 1;
                        else sum_ok <= 0;
                            sum_value[7:0] <= 8'b00000000; //reset the sum value
                            m <= 0; //reset the word counter    
                    end
                   else begin
                            sum_value[7:0] <= sum_value[7:0] + data[7:0];
                           // $display ("sum_value vale %b al tempo:", sum_value, $time);
                            m <= m + 1;
                            sum_ok <= 0;

                   end 
              end     
       end

endmodule

ahora me devuelven 10 caracteres, siempre los mismos 10 caracteres, que no son los 10 que envío. ( parcialmente correcto )

SI y cuando la suma de comprobación que envío no es correcta, mi máquina no transmite ( correcto ).
La siguiente parte es encontrar por qué la memoria se inicializa con valores que no son los que deseo.
Actualmente utilizamos la simulación timing posterior a la implementación, que parece ser mucho más precisa que funcional, aunque muy lenta.

Estoy dedicando mi tiempo a analizar las formas de onda para ver si el receptor recibe cada bit (y byte, es decir, char) correctamente y si lo envía correctamente a la memoria, y sobre lo que está haciendo la memoria para detectar el no enviado y aparentemente bytes de contenido un tanto aleatorios.

    
respondido por el Michele Marconi

Lea otras preguntas en las etiquetas