Electronic – How to send a packet every n clock cycles in verilog

clockdelayrtlstate-machinesverilog

I am fairly new to Verilog and in general Digital Design. I am working on a project which has a state machine. The module, in a particular state, receives a read request packet from some other module and I have to decode the required read response data size and send back as many data bytes in the payload. Now I have to do this every n clock cycles.

I am finding it difficult to understand where should my count be updated and where should it be assigned the next count value? Here is the general structure of my code.

    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

This is the sequential block.Now in my combinational block

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

I am unable to understand does this wait the required count no of cycles and then update the reg values or I am missing something here.

I would very much appreciate some clarity about this.

P.S. Pardon me if I am missing some fundamental aspects of behavioural design regarding difference between sequential and combinational blocks.

Here is a complete code of what I am trying to do. Essentially I want to read a packet, turn of reading of packets, wait 7 or 8 clock cycles or N clk cycles and then send a read response with the data in the registers which are assigned to output ports continuosly.

    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

Best Answer

There are a couple of things to point out in your code.

1) you're driving the reg_read_en and reg_write_en signals in both always blocks.

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

and

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 

This isn't synthesizable and probably wouldn't be allowed by some simulation tools either.

2) You're inferring latches in your combinitorial blocks:

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 

The marked if statement needs a corresponding else in every case. You need to cover all the different possible options.

This is why in my comment I suggested you use a single sequential block, it'll fix both of these issues, and make everything a lot easier to keep track of.

So here's an example of combining them:

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