¿Cómo enviar un paquete cada n ciclos de reloj en verilog?

2

Soy bastante nuevo en Verilog y en general Diseño Digital. Estoy trabajando en un proyecto que tiene una máquina de estado. El módulo, en un estado particular, recibe un paquete de solicitud de lectura de otro módulo y tengo que descodificar el tamaño de datos de respuesta de lectura requerido y devolver tantos bytes de datos en la carga útil. Ahora tengo que hacer esto cada n ciclos.

Me resulta difícil entender dónde se debe actualizar mi conteo y dónde se debe asignar el siguiente valor de conteo. Aquí está la estructura general de mi código.

    always@(posedge clk_i or negedge resetn_i) begin
    if (resetn_i == 1'b0) begin
        reg_read_en         <= 1'b0;
        reg_write_en        <= 1'b0;
        count               <= 3'b111;
        next_count          <= 3'b111;  
        state               <= S_INITIAL;

    end else begin
        state               <= next_state;
        count               <= next_count;
    end

Este es el bloque secuencial. Ahora en mi bloque combinacional

always@(*) begin

    next_state  = state;
    next_count  = count;
    .......
    ........
    M_PASSIVE:begin                         
                        if(count == 3'b000)
                        begin
                           //Update some registers with new data values
                        end else begin
                        next_count = count - 3'b001;
                        end

No puedo entender. Espera el recuento requerido de ciclos y luego actualizo los valores de registro o me estoy perdiendo algo aquí.

Apreciaría mucho algo de claridad sobre esto.

P.S. Perdóneme si me faltan algunos aspectos fundamentales del diseño de comportamiento con respecto a la diferencia entre bloques secuenciales y combinacionales.

Aquí hay un código completo de lo que estoy tratando de hacer. Esencialmente quiero leer un paquete, girar la lectura de paquetes, esperar 7 u 8 ciclos de reloj o N clk ciclos y luego enviar una respuesta de lectura con los datos en los registros que se asignan a los puertos de salida continuamente.

    always@(posedge clk_i or negedge resetn_i) begin
    if (resetn_i == 1'b0) begin
        reg_read_en         <= 1'b0;
        reg_write_en        <= 1'b0;
        count           <= 3'b111;
        state           <= S_INITIAL;
    end else begin
        state           <= next_state;
        count           <= next_count;
    end 
end 

always@(*) begin
    next_state  = state;
    next_count  = count;
    case(state)
        S_INITIAL: begin //not enabled
            if(enable_i == 1'b1) begin                  
                next_state = S_ENABLED;
            end
            reg_read_en         <= 1'b0;
            reg_write_en        <= 1'b0;
        end 

        S_ENABLED: begin
            if(enable_i == 1'b0) begin
                next_state = S_INITIAL;                 
            end

            case(mode_i)
                M_PASSIVE:begin
                    reg_read_en =   dnoc_packet_ready;
                    //pkt_type  =   dnoc_packet_pld_header[]
                    if(dnoc_packet_ready)
                    begin
                        reg_read_en = 1'b0;
                        if(count == 3'b000)
                        begin
                        reg_write_en    =   1'b1; 
                        read_resp_tx_data_size = 3'b001;     
                        read_resp_tx_qpe_dest  = 6'b000010;      
                        read_resp_tx_mod_dest   = 5'b00000;
                        read_resp_tx_c_routing  = 0;
                        read_resp_tx_pld_header = 17'b00100101000000000;
                        read_resp_tx_pld_address    = 32'h0001;   
                        read_resp_tx_pld_data   = 256;
                                                        //count = 3'b111; 
                        reg_write_en    =   1'b0;
                        end
                        next_count = count - 3'b001;
                    end
                end
    
pregunta frisco_1989

2 respuestas

0

Hay un par de cosas para señalar en su código.

1) estás manejando las señales reg_read_en y reg_write_en en ambos bloques siempre.

always@(posedge clk_i or negedge resetn_i) begin
if (resetn_i == 1'b0) begin
    reg_read_en         <= 1'b0; <=
    reg_write_en        <= 1'b0; <=
    count               <= 3'b111;
    next_count          <= 3'b111;  
    state               <= S_INITIAL;

end else begin
    state               <= next_state;
    count               <= next_count;
end

y

always@(*) begin
next_state  = state;
next_count  = count;
case(state)
    S_INITIAL: begin //not enabled
        if(enable_i == 1'b1) begin                  
            next_state = S_ENABLED;
        end
        reg_read_en         <= 1'b0; <==
        reg_write_en        <= 1'b0; <==
    end 

Esto no es sintetizable y probablemente tampoco lo permitan algunas herramientas de simulación.

2) Estás cerrando los cierres en tus bloques de combinación:

always@(*) begin
next_state  = state;
next_count  = count;
case(state)
    S_INITIAL: begin //not enabled
        if(enable_i == 1'b1) begin         <==         
            next_state = S_ENABLED;        <==
        end                                <==
        reg_read_en         <= 1'b0;
        reg_write_en        <= 1'b0;
    end 

La instrucción if marcada necesita un else correspondiente en cada caso. Necesitas cubrir todas las diferentes opciones posibles.

Esta es la razón por la que en mi comentario le sugerí que usara un solo bloque secuencial, solucionará ambos problemas y hará que sea mucho más fácil hacer un seguimiento de todo.

Este es un ejemplo de cómo combinarlos:

always@(posedge clk_i or negedge resetn_i) begin
  if (resetn_i == 1'b0) begin
     reg_read_en              <= 1'b0;
     reg_write_en             <= 1'b0;
     count                    <= 3'b111;
     state                    <= S_INITIAL;

     // I was trained to give everything a reset value
     // But that's not really necessary and is more a matter of style
     // as long as it gets a value when it needs one.
     // including this here for completeness
     read_resp_tx_data_size   <= 3'b000;
     read_resp_tx_qpe_dest    <= 6'b000000;
     read_resp_tx_mod_dest    <= 5'b00000;
     read_resp_tx_c_routing   <= 0;
     read_resp_tx_pld_header  <= 17'b00000000000000000;
     read_resp_tx_pld_address <= 32'h0000;
     read_resp_tx_pld_data    <= 0;

  end
  else begin

     case(state)
       S_INITIAL: begin //not enabled
          if(enable_i == 1'b1) begin
             state <= S_ENABLED;

             // set count to the initial value here
             // this way, you re-set it every time you switch to enabled
             count      <= 3'b111;


             // If there's anything else you want to setup, here's a good place to do it...

          end
          else begin
             state <= S_INITIAL;

          end

          reg_read_en  <= 1'b0;
          reg_write_en <= 1'b0;

       end

       S_ENABLED: begin
          if(enable_i == 1'b0) begin
             state <= S_INITIAL;

             // here's where you would clear any signals that you want to clear before going back to the initial state


          end
          else begin
             state <= S_ENABLED;

          end

          case(mode_i)
            M_PASSIVE:begin
               reg_read_en <= dnoc_packet_ready;

               //pkt_type  =   dnoc_packet_pld_header[]
               if(dnoc_packet_ready) begin
                  reg_read_en <= 1'b0;

                  if(count == 3'b000) begin
                     reg_write_en             <= 1'b1;
                     read_resp_tx_data_size   <= 3'b001;
                     read_resp_tx_qpe_dest    <= 6'b000010;
                     read_resp_tx_mod_dest    <= 5'b00000;
                     read_resp_tx_c_routing   <= 0;
                     read_resp_tx_pld_header  <= 17'b00100101000000000;
                     read_resp_tx_pld_address <= 32'h0001;
                     read_resp_tx_pld_data    <= 256;

                     // you can reset count here to 7 if you want it to start again
                     count                    <= 3'b111;


                  end
                  else begin

                     // this will make reg_write_en valid only when count is 0
                     // and it will be disabled the rest of the time.
                     // if you want it valid all the time, you can set this to 1 and clear it elsewhere.
                     reg_write_en <= 1'b0;



                     // this stops the counter from underflowing
                     // otherwise, when it's 0 it will underflow to 111.
                     // Unless that's what you want, of course.
                     count        <= count - 3'b001;

                  end

               end
            end
            // other cases...
          endcase

       endcase
  end
end
    
respondido por el stanri
1

Estás muy cerca. El error es que 'next_count' solo se actualiza en el bloque combinatorio always @ (*) y 'count' solo se actualiza en la parte registrada / registrada always @ (posedge clk_i ... .

Por lo tanto, elimine next_count < = 3'b111;

No puedo ver el resto de tu código, así que comienza con eso.

No dijiste cuántos ciclos tienes que esperar: ¿7 u 8? Seré honesto. La mayoría de las veces escribo un código que me lleva al 99% del camino. Si los datos se almacenan después de 7 u 8 ciclos, los detalles se resuelven tan fácilmente que a menudo ejecuto primero la simulación y luego hago una corrección de +1 o -1.

Publicar edición después de nuevo código:
No puedo depurar su máquina de estado para usted, pero puedo hacer algunos comentarios. Establece reg_write_en y algunas líneas más abajo luego lo borras de nuevo.

Supongo que se supone que esto realmente funciona en hardware, no solo en simulación. Si es así su código produce muchos pestillos. No se le notificará de ellos hasta que resuma el código. Por ejemplo estos:

read_resp_tx_qpe_dest      
read_resp_tx_mod_dest  
read_resp_tx_c_routing 
reg_read_en
reg_write_en

En su sección combinatoria siempre @ (*) debe asignar un valor a cada variable en cada estado. Si no consigues pestillos. Una forma de hacerlo es lo que hace con next_state y next_count : establezca un valor predeterminado en la parte superior. Pero también es una buena práctica documentar que es un valor predeterminado que se cambiará más adelante.

// Defaults:
next_state  = state;
next_count  = count;
reg_write_en = 1'b0; 
reg_read_en = 1'b0;

Los dos primeros deben permanecer iguales si no se tocan.
Los otros dos son ejemplos de variables que solo se establecen en uno o dos estados y son cero en cualquier otra parte.
En cuanto a sus variables 'read_resp_tx ...': no sé qué más se hace con ellas, por lo que debe decidir cómo manejarlas.

En cuanto a dividir su código en un registro, una sección combinatoria: eso depende de usted. Con lo que te sientas cómodo. Tiendo a hacerlo mucho a menos que sea una máquina de estado trivial. He visto el código de una gran compañía de IP que proporciona millones de núcleos de CPU en todo el mundo y lo usan en todas partes . Sospecho que es obligatorio en su guía de estilo de codificación.

    
respondido por el Oldfart

Lea otras preguntas en las etiquetas