FPGA project: DHT11

Mistakes I made:

1. When the START state jumps to the REPLAY state, sda will jump from high level to low level. This falling edge is caused by the sensor producing a low level response. Rather than being generated when the sensor is ready to end at a high level.
REPLAYtoWAIT_2S = ( state_c == REPLAY ) & amp; & amp; (nege & amp; & amp; flag0 == 1’b0 & amp; & amp; flag1 == 1’b0 & amp; & amp; cnt_us >= 30 ) ; // Missed here, cnt_us >= 30 is added later;

2. RD_DATAtoWAIT_2S = (state_c == RD_DATA) & amp; & amp; (pose & amp; & amp; data_done == 1); // Wrong writing, data_done should be used. Flag_base was added before. This causes the state machine to get stuck in the RD_DATA state. Because pose is random. Although synchronous beat synchronization is performed and it is in the same clock domain as flag_base, it is almost impossible for them to be satisfied at the same time.

Summary: In projects with relatively simple timing, the most likely error-prone part is the state machine, and the most likely error-prone part of the state machine is the description of state transition conditions. Therefore, you must be extra cautious when designing state machines in the future.

3. A warning during compilation:

This is caused by the state machine forgetting to change the value when defining the state parameters.

4. Signals optimized by quartus.

In Quartus, use statements such as /*synthesis noprune*/, /*synthesis preserve*/, etc. Note that if these statements are used to define signals, they need to be placed at the end of the definition statement.

In Vivado, use (* keep=”true” *), (* keep_hierarchy=”yes” *) statements. These statements are placed before the signal definition

module dht11(
    input wire sys_clk,
    input wire sys_rst_n ,
    input wire key_in,

    inout wire sda,

    output reg [19:0] data_out ,
    output reg sign
);

    // localparam
    localparam WAIT_2S = 4'b0001 ,
                        START = 4'b0010,
                        REPLAY = 4'b0100 ,
                        RD_DATA = 4'b1000;
    // wire signal define
    wire nege;
    wire pose;
    wire flag_base;
    wire WAIT_2StoSTART;
    wire STARTtoREPLAY;
    wire REPLAYtoRD_DATA;
    wire REPLAYtoWAIT_2S;
    wire RD_DATAtoWAIT_2S;
    //reg signal define
    reg sda_reg0;
    reg sda_reg1;
    reg sda_reg2;
    reg sda_en;
    reg sda_out;
    reg flag0 ;
    reg flag1;
    reg key_flag;
    reg data_done;
    reg [5:0] cnt_base;
    reg [3:0] state_c /*synthesis preserve*/;
    reg [3:0] state_n;
    reg [5:0] cnt_bit;
    reg [20:0] cnt_us ;
    reg [39:0] data_temp;

/****************************************************** ************************/
    //Generation of 1us flag signal
    // reg [5:0] cnt_base ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cnt_base <= 6'd0;
        end else begin
            if(flag_base) begin
                cnt_base <= 6'd0;
            end else begin
                cnt_base <= cnt_base + 1'b1;
            end
        end
    end
    // wire flag_base;
    assign flag_base = (cnt_base == 49) ? 1'b1 : 1'b0 ;

    // Synchronize and beat twice
    // reg sda_reg0 // reg sda_reg1 // reg sda_reg2;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            sda_reg0 <= 1'b1;
            sda_reg1 <= 1'b1;
            sda_reg2 <= 1'b1;
        end else begin
            sda_reg0 <= sda;
            sda_reg1 <= sda_reg0;
            sda_reg2 <= sda_reg1;
        end
    end

    // Detection of rising and falling edges of sda bus
    // wire nege;
    // wire pose ;
    assign nege = ~sda_reg1 & amp; & amp; sda_reg2 ;
    assign pose = sda_reg1 & amp; & amp; ~sda_reg2 ;

    //Three-stage state machine, state transition description, state transition condition description, output description (description of quantities related to state transition)
    //reg [4:0] state_c;
    //reg [4:0] state_n;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            state_c <= WAIT_2S;
        end else begin
            state_c <= state_n;
        end
    end
    always @(*) begin
        case(state_c)
        WAIT_2S:begin
                    if(WAIT_2StoSTART) begin
                        state_n <= START ;
                    end else begin
                        state_n <= WAIT_2S;
                    end
                end
        START:begin
                    if(STARTtoREPLAY) begin
                        state_n <= REPLAY ;
                    end else begin
                        state_n <= START ;
                    end
                end
        REPLAY :begin
                    if(REPLAYtoRD_DATA) begin
                        state_n <= RD_DATA;
                    end else begin
                        if(REPLAYtoWAIT_2S) begin
                            state_n <= WAIT_2S;
                        end else begin
                            state_n <= REPLAY ;
                        end
                    end
                end
        RD_DATA:begin
                    if(RD_DATAtoWAIT_2S) begin
                        state_n <= WAIT_2S;
                    end else begin
                        state_n <= RD_DATA;
                    end
                end
        default: state_n <= WAIT_2S;
        endcase
    end
    assign WAIT_2StoSTART = ( state_c == WAIT_2S ) & amp; & amp; (flag_base & amp; & amp; cnt_us == 1_999_999) ;
    assign STARTtoREPLAY = ( state_c == START ) & amp; & amp; (flag_base & amp; & amp; cnt_us == 20_012 ) ;
    assign REPLAYtoRD_DATA = ( state_c == REPLAY ) & amp; & amp; (nege & amp; & amp; flag0 & amp; & amp; flag1) ;
    assign REPLAYtoWAIT_2S = ( state_c == REPLAY ) & amp; & amp; (nege & amp; & amp; flag0 == 1'b0 & amp; & amp; flag1 == 1'b0 & amp; & amp; cnt_us >= 30) ; // Missed once here, cnt_us >= 30 is added later; because the first low level is the sensor pulling low in response to the low level, not the end of the low level in response to the high level.
    assign RD_DATAtoWAIT_2S = (state_c == RD_DATA) & amp; & amp; (pose & amp; & amp; data_done == 1); // Wrong writing, data_done should be used. Moreover, pose and flag_base are in two clock domains and cannot be satisfied at the same time. logical error.
    // reg [20:0] cnt_us ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cnt_us <= 21'd0;
        end else begin
            case(state_c)
            WAIT_2S:begin
                        if(flag_base & amp; & amp; cnt_us == 1_999_999) begin
                            cnt_us <= 21'd0;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
            START:begin
                        if(flag_base & amp; & amp; cnt_us == 20_012) begin
                            cnt_us <= 21'd0;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
            REPLAY :begin
                        if(nege || pose) begin
                            cnt_us <= 21'd0;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
            RD_DATA:begin
                        if(nege || pose) begin
                            cnt_us <= 21'd0;
                        end else begin
                            if(flag_base) begin
                                cnt_us <= cnt_us + 1'b1;
                            end else begin
                                cnt_us <= cnt_us ;
                            end
                        end
                    end
                default: cnt_us <= 21'd0;
            endcase
        end
    end
    // reg flag0 ;
    // reg flag1 ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            flag0 <= 1'b0;
            flag1 <= 1'b0;
        end else begin
            if(state_c == REPLAY) begin
                if(cnt_us >= 81 & amp; & sda == 0) begin // There may actually be a small problem here, that is, the low level of the response timing is lower than 81 and is raised, but the high level of the response timing is satisfied. Then it will also be judged as a satisfactory response.
                    flag0 <= 1'b1;
                end else begin
                    flag0 <= flag0; // Therefore, when designing the flag0 signal, you have to add one, sda == 0
                end

                if(cnt_us >= 85 & amp; & sda == 1) begin
                    flag1 <= 1'b1;
                end else begin
                    flag1 <= flag1 ;
                end
            end else begin
                flag0 <= 1'b0;
                flag1 <= 1'b0;
            end
        end
    end
    // reg [5:0] cnt_bit ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            cnt_bit <= 6'd0;
        end else begin
            if(state_c == RD_DATA) begin
                if(nege) begin // Determining the value of cnt_us is something to consider when assigning a value to data_temp. Here is just cnt_bit + 1
                    cnt_bit <= cnt_bit + 1'b1;
                end else begin
                    if(pose & amp; & amp; cnt_bit == 40) begin
                        cnt_bit <= 6'd0;
                    end else begin
                        cnt_bit <= cnt_bit ;
                    end
                end
            end else begin
                cnt_bit <= 6'd0;
            end
        end
    end
    //reg data_done;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            data_done <= 1'b0;
        end else begin
            if(state_c == RD_DATA & amp; & amp; cnt_bit == 40) begin
                data_done <= 1'b1;
            end else begin
                data_done <= 0 ;
            end
        end
    end
    //reg [39:0] data_temp;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            data_temp <= 40'd0;
        end else begin
            if(state_c == RD_DATA) begin
                if(nege) begin
                    if(cnt_us >= 23 & amp; & amp; cnt_us <= 27) begin
                        data_temp[39 - cnt_bit] <= 1'b0;
                    end else begin
                        if(cnt_us >= 68 & amp; & amp; cnt_us <= 74) begin
                            data_temp[39 - cnt_bit] <= 1'b1;
                        end else begin
                            data_temp[39 - cnt_bit] <= 1'b0;
                        end
                    end
                end else begin
                    data_temp <= data_temp;
                end
            end
        end
    end
    //reg key_flag;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            key_flag <= 1'b0;
        end else begin
            if(key_in) begin
                key_flag <= ~key_flag;
            end else begin
                key_flag <= key_flag;
            end
        end
    end
    //Three-state enable and output
    //reg sda_en;
    //reg sda_out;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            sda_en <= 1'b1;
            sda_out <= 1'b1;
        end begin
            case (state_c)
            WAIT_2S : begin
                           sda_en <= 1'b1;
                           sda_out <= 1'b1;
                        end // Due to the use of reg type, the high level will be raised for 20ns in the start state.
            START: begin
                            sda_en <= 1'b1;
                            if(cnt_us <= 19_999) begin
                                sda_out <= 1'b0;
                            end else begin
                                sda_out <= 1'b1;
                            end
                        end
            REPLAY: begin
                            sda_en <= 1'b0;
                            sda_out <= 1'b1;
                        end
            RD_DATA : begin
                            sda_en <= 1'b0;
                            sda_out <= 1'b1;
                        end
                default: begin
                            sda_en <= 1'b0;
                            sda_out <= 1'b1;
                        end
            endcase
        end
    end
    
/************************************output signal****************** ****************************/
    //wire sda,
    assign sda = (sda_en == 1'b1) ? sda_out : 1'bz ; // Note the three-state output assignment z
    // reg [19:0] data_out ,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            data_out <= 20'd0;
        end else begin
            if(key_flag == 1'b0) begin // Display humidity. The first 8 digits are integers and the last 8 digits are decimals.
                data_out <= (data_temp[39:24] >> 8) * 20'd10; // Because the last digit of the digital tube displays decimals, ×10.
            end else begin
                data_out <= data_temp[23:16] * 20'd10 + data_temp[11:8];
            end
        end
    end
    // At present, the DHT11 temperature can only be accurate to 0.1℃, so the value of the temperature 8-bit decimal data is less than 10.
    // We use the lower four digits of decimal data to represent it when applying
    //The decimal value of the temperature, the highest digit indicates the positive or negative temperature.
    //reg sign
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n) begin
            sign <= 1'b0;
        end else begin
            if(data_done & amp; & amp; (data_temp[7:0] == (data_temp[15:8] + data_temp[23:16] + data_temp[31:24] + data_temp[39:32]))) begin
                if(data_temp[15] == 1'b1) begin
                    sign <= 1'b1;
                end else begin
                    sign <= 1'b0;
                end
            end
        end
    end

endmodule
module top(
    input wire sys_clk,
    input wire sys_rst_n ,
    input wire key,

    inout wire dht11 ,

    output wire ds,
    output wire oe,
    output wire shcp,
    output wire stcp
);

    // Instantiate the connection
    wire key_out_w;
    wire[19:00] data_w;
    wire sign_w;
    wire [5: 0] point_w;
    wire en_w;
    assign point_w = 6'b000_010;
    assign en_w = 1'b1;

key_filter key_filter_insert(
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n),
    .key_in ( key ) ,

    .key_out ( key_out_w )
);

dht11 dht11_insert(
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n),
    .key_in ( key_out_w ) ,

    .sda(dht11),

    .data_out (data_w),
    .sign ( sign_w )
);

seg_595_dynamic seg_595_dynamic_insert(
    .sys_clk (sys_clk),
    .sys_rst_n (sys_rst_n),
    .data (data_w) ,
    .point(point_w),
    .sign ( sign_w ) ,
    .seg_en ( en_w ) ,

    .ds ( ds ) ,
    .oe (oe) ,
    .shcp ( shcp ) ,
    .stcp ( stcp )
);

endmodule

There is no simulation, only the waveform captured by signaltap, but I forgot to take a screenshot.