Pmodで接続したロータリエンコーダの回転を読み取る回路を,Verylコードとして生成させます.

ターゲットデバイスは, Tang Primer 25K です.エンコーダの1クリックごとにカウンタをカウントアップさせ,7セグLED に表示します.

 GPT-5.5を使いました.一部 Veryl の構文を正しく解釈できていないので,手動で修正して veryl build で SV にトランスレート後, Gowin IDE で合成します.

Verilog
module Top #(
    param CLK_HZ          : u32 = 50_000_000,
    param ENCODER_FILTER  : u32 = 16_384    ,
    param SCAN_DIVIDER_MAX: u32 = 8_333     ,
) (
    i_clk       : input  clock   ,
    i_enc_a     : input  logic   ,
    i_enc_b     : input  logic   ,
    i_push_sw   : input  reset   ,
    i_slide_sw  : input  logic   ,
    o_seg_anode : output logic<8>,
    o_digit_cath: output logic<6>,
) {
    var enc_a_meta      : logic    ;
    var enc_a_sync      : logic    ;
    var enc_b_meta      : logic    ;
    var enc_b_sync      : logic    ;
    var slide_meta      : logic    ;
    var slide_sync      : logic    ;
    var enc_raw         : logic<2> ;
    var enc_stable      : logic<2> ;
    var enc_stable_prev : logic<2> ;
    var enc_filter_count: logic<15>;
    var scan_count      : logic<14>;
    var scan_digit      : logic<3> ;
    var digit0          : logic<4> ;
    var digit1          : logic<4> ;
    var digit2          : logic<4> ;
    var digit3          : logic<4> ;
    var digit4          : logic<4> ;
    var digit5          : logic<4> ;
    var current_digit   : logic<4> ;
    var step_up         : logic    ;
    var step_down       : logic    ;

    assign enc_raw = {enc_a_sync, enc_b_sync};

    always_ff (i_clk, i_push_sw) {
        if_reset {
            enc_a_meta = 0;
            enc_a_sync = 0;
            enc_b_meta = 0;
            enc_b_sync = 0;
            slide_meta = 0;
            slide_sync = 0;
        } else {
            enc_a_meta = i_enc_a;
            enc_a_sync = enc_a_meta;
            enc_b_meta = i_enc_b;
            enc_b_sync = enc_b_meta;
            slide_meta = i_slide_sw;
            slide_sync = slide_meta;
        }
    }

    always_ff (i_clk, i_push_sw) {
        if_reset {
            enc_stable       = 0;
            enc_filter_count = 0;
        } else if enc_raw == enc_stable {
            enc_filter_count = 0;
        } else if enc_filter_count == (ENCODER_FILTER - 1) {
            enc_stable       = enc_raw;
            enc_filter_count = 0;
        } else {
            enc_filter_count += 1;
        }
    }

    always_comb {
        step_up   = 0;
        step_down = 0;

        case {enc_stable_prev, enc_stable} {
            4'b0001, 4'b0111, 4'b1110, 4'b1000: step_up   = ~slide_sync;
            4'b0010, 4'b1011, 4'b1101, 4'b0100: step_down = ~slide_sync;
            default                           : {}
        }

        case {enc_stable_prev, enc_stable} {
            4'b0001, 4'b0111, 4'b1110, 4'b1000: step_down = slide_sync;
            4'b0010, 4'b1011, 4'b1101, 4'b0100: step_up   = slide_sync;
            default                           : {}
        }
    }

    always_ff (i_clk, i_push_sw) {
        if_reset {
            enc_stable_prev = 0;
            digit0          = 0;
            digit1          = 0;
            digit2          = 0;
            digit3          = 0;
            digit4          = 0;
            digit5          = 0;
        } else {
            enc_stable_prev = enc_stable;

            if step_up {
                if digit0 == 9 {
                    digit0 = 0;
                    if digit1 == 9 {
                        digit1 = 0;
                        if digit2 == 9 {
                            digit2 = 0;
                            if digit3 == 9 {
                                digit3 = 0;
                                if digit4 == 9 {
                                    digit4 = 0;
                                    if digit5 == 9 {
                                        digit5 = 0;
                                    } else {
                                        digit5 += 1;
                                    }
                                } else {
                                    digit4 += 1;
                                }
                            } else {
                                digit3 += 1;
                            }
                        } else {
                            digit2 += 1;
                        }
                    } else {
                        digit1 += 1;
                    }
                } else {
                    digit0 += 1;
                }
            } else if step_down {
                if digit0 == 0 {
                    digit0 = 9;
                    if digit1 == 0 {
                        digit1 = 9;
                        if digit2 == 0 {
                            digit2 = 9;
                            if digit3 == 0 {
                                digit3 = 9;
                                if digit4 == 0 {
                                    digit4 = 9;
                                    if digit5 == 0 {
                                        digit5 = 9;
                                    } else {
                                        digit5 -= 1;
                                    }
                                } else {
                                    digit4 -= 1;
                                }
                            } else {
                                digit3 -= 1;
                            }
                        } else {
                            digit2 -= 1;
                        }
                    } else {
                        digit1 -= 1;
                    }
                } else {
                    digit0 -= 1;
                }
            }
        }
    }

    always_ff (i_clk, i_push_sw) {
        if_reset {
            scan_count = 0;
            scan_digit = 0;
        } else if scan_count == (SCAN_DIVIDER_MAX - 1) {
            scan_count = 0;
            if scan_digit == 5 {
                scan_digit = 0;
            } else {
                scan_digit += 1;
            }
        } else {
            scan_count += 1;
        }
    }

    always_comb {
        current_digit = 0;
        o_digit_cath  = 6'b111111;

        case scan_digit {
            0: {
                current_digit = digit0;
                o_digit_cath  = 6'b111110;
            }
            1: {
                current_digit = digit1;
                o_digit_cath  = 6'b111101;
            }
            2: {
                current_digit = digit2;
                o_digit_cath  = 6'b111011;
            }
            3: {
                current_digit = digit3;
                o_digit_cath  = 6'b110111;
            }
            4: {
                current_digit = digit4;
                o_digit_cath  = 6'b101111;
            }
            5: {
                current_digit = digit5;
                o_digit_cath  = 6'b011111;
            }
            default: {}
        }

        case current_digit {
            0      : o_seg_anode = 8'b00111111;
            1      : o_seg_anode = 8'b00000110;
            2      : o_seg_anode = 8'b01011011;
            3      : o_seg_anode = 8'b01001111;
            4      : o_seg_anode = 8'b01100110;
            5      : o_seg_anode = 8'b01101101;
            6      : o_seg_anode = 8'b01111101;
            7      : o_seg_anode = 8'b00000111;
            8      : o_seg_anode = 8'b01111111;
            9      : o_seg_anode = 8'b01101111;
            default: o_seg_anode = 8'b00000000;
        }
    }
}
Expand

RTLコードを確認したところ,チャタリング対策がなされていません.

ロータリエンコーダのA相/B相の信号にデバウンス回路を付加したものを再生成させます.

Verilog
module Top #(
    param CLK_HZ          : u32 = 50_000_000,
    param DEBOUNCE_CYCLES : u32 = 250_000   ,
    param SCAN_DIVIDER_MAX: u32 = 8_333     ,
) (
    i_clk       : input  clock   ,
    i_enc_a     : input  logic   ,
    i_enc_b     : input  logic   ,
    i_push_sw   : input  reset   ,
    i_slide_sw  : input  logic   ,
    o_seg_anode : output logic<8>,
    o_digit_cath: output logic<6>,
) {
    var enc_a_meta     : logic    ;
    var enc_a_sync     : logic    ;
    var enc_b_meta     : logic    ;
    var enc_b_sync     : logic    ;
    var slide_meta     : logic    ;
    var slide_sync     : logic    ;
    var enc_a_debounced: logic    ;
    var enc_b_debounced: logic    ;
    var enc_a_db_count : logic<20>;
    var enc_b_db_count : logic<20>;
    var enc_state      : logic<2> ;
    var enc_state_prev : logic<2> ;
    var scan_count     : logic<14>;
    var scan_digit     : logic<3> ;
    var digit0         : logic<4> ;
    var digit1         : logic<4> ;
    var digit2         : logic<4> ;
    var digit3         : logic<4> ;
    var digit4         : logic<4> ;
    var digit5         : logic<4> ;
    var current_digit  : logic<4> ;
    var step_up        : logic    ;
    var step_down      : logic    ;

    assign enc_state = {enc_a_debounced, enc_b_debounced};

    always_ff (i_clk, i_push_sw) {
        if_reset {
            enc_a_meta = 0;
            enc_a_sync = 0;
            enc_b_meta = 0;
            enc_b_sync = 0;
            slide_meta = 0;
            slide_sync = 0;
        } else {
            enc_a_meta = i_enc_a;
            enc_a_sync = enc_a_meta;
            enc_b_meta = i_enc_b;
            enc_b_sync = enc_b_meta;
            slide_meta = i_slide_sw;
            slide_sync = slide_meta;
        }
    }

    always_ff (i_clk, i_push_sw) {
        if_reset {
            enc_a_debounced = 0;
            enc_a_db_count  = 0;
            enc_b_debounced = 0;
            enc_b_db_count  = 0;
        } else {
            if enc_a_sync == enc_a_debounced {
                enc_a_db_count = 0;
            } else if enc_a_db_count == (DEBOUNCE_CYCLES - 1) {
                enc_a_debounced = enc_a_sync;
                enc_a_db_count  = 0;
            } else {
                enc_a_db_count += 1;
            }

            if enc_b_sync == enc_b_debounced {
                enc_b_db_count = 0;
            } else if enc_b_db_count == (DEBOUNCE_CYCLES - 1) {
                enc_b_debounced = enc_b_sync;
                enc_b_db_count  = 0;
            } else {
                enc_b_db_count += 1;
            }
        }
    }

    always_comb {
        step_up   = 0;
        step_down = 0;

        case {enc_state_prev, enc_state} {
            4'b0001, 4'b0111, 4'b1110, 4'b1000: step_up   = ~slide_sync;
            4'b0010, 4'b1011, 4'b1101, 4'b0100: step_down = ~slide_sync;
            default                           : {}
        }

        case {enc_state_prev, enc_state} {
            4'b0001, 4'b0111, 4'b1110, 4'b1000: step_down = slide_sync;
            4'b0010, 4'b1011, 4'b1101, 4'b0100: step_up   = slide_sync;
            default                           : {}
        }
    }

    always_ff (i_clk, i_push_sw) {
        if_reset {
            enc_state_prev = 0;
            digit0         = 0;
            digit1         = 0;
            digit2         = 0;
            digit3         = 0;
            digit4         = 0;
            digit5         = 0;
        } else {
            enc_state_prev = enc_state;

            if step_up {
                if digit0 == 9 {
                    digit0 = 0;
                    if digit1 == 9 {
                        digit1 = 0;
                        if digit2 == 9 {
                            digit2 = 0;
                            if digit3 == 9 {
                                digit3 = 0;
                                if digit4 == 9 {
                                    digit4 = 0;
                                    if digit5 == 9 {
                                        digit5 = 0;
                                    } else {
                                        digit5 += 1;
                                    }
                                } else {
                                    digit4 += 1;
                                }
                            } else {
                                digit3 += 1;
                            }
                        } else {
                            digit2 += 1;
                        }
                    } else {
                        digit1 += 1;
                    }
                } else {
                    digit0 += 1;
                }
            } else if step_down {
                if digit0 == 0 {
                    digit0 = 9;
                    if digit1 == 0 {
                        digit1 = 9;
                        if digit2 == 0 {
                            digit2 = 9;
                            if digit3 == 0 {
                                digit3 = 9;
                                if digit4 == 0 {
                                    digit4 = 9;
                                    if digit5 == 0 {
                                        digit5 = 9;
                                    } else {
                                        digit5 -= 1;
                                    }
                                } else {
                                    digit4 -= 1;
                                }
                            } else {
                                digit3 -= 1;
                            }
                        } else {
                            digit2 -= 1;
                        }
                    } else {
                        digit1 -= 1;
                    }
                } else {
                    digit0 -= 1;
                }
            }
        }
    }

    always_ff (i_clk, i_push_sw) {
        if_reset {
            scan_count = 0;
            scan_digit = 0;
        } else if scan_count == (SCAN_DIVIDER_MAX - 1) {
            scan_count = 0;
            if scan_digit == 5 {
                scan_digit = 0;
            } else {
                scan_digit += 1;
            }
        } else {
            scan_count += 1;
        }
    }

    always_comb {
        current_digit = 0;
        o_digit_cath  = 6'b111111;

        case scan_digit {
            0: {
                current_digit = digit0;
                o_digit_cath  = 6'b111110;
            }
            1: {
                current_digit = digit1;
                o_digit_cath  = 6'b111101;
            }
            2: {
                current_digit = digit2;
                o_digit_cath  = 6'b111011;
            }
            3: {
                current_digit = digit3;
                o_digit_cath  = 6'b110111;
            }
            4: {
                current_digit = digit4;
                o_digit_cath  = 6'b101111;
            }
            5: {
                current_digit = digit5;
                o_digit_cath  = 6'b011111;
            }
            default: {}
        }

        case current_digit {
            0      : o_seg_anode = 8'b00111111;
            1      : o_seg_anode = 8'b00000110;
            2      : o_seg_anode = 8'b01011011;
            3      : o_seg_anode = 8'b01001111;
            4      : o_seg_anode = 8'b01100110;
            5      : o_seg_anode = 8'b01101101;
            6      : o_seg_anode = 8'b01111101;
            7      : o_seg_anode = 8'b00000111;
            8      : o_seg_anode = 8'b01111111;
            9      : o_seg_anode = 8'b01101111;
            default: o_seg_anode = 8'b00000000;
        }
    }
}
Expand

コメントを残す