{"id":1078,"date":"2025-10-11T22:27:29","date_gmt":"2025-10-11T14:27:29","guid":{"rendered":"https:\/\/vm1.go2see.me\/?p=1078"},"modified":"2025-10-12T20:43:22","modified_gmt":"2025-10-12T12:43:22","slug":"1078","status":"publish","type":"post","link":"https:\/\/vm1.go2see.me\/?p=1078","title":{"rendered":"Key-Comb Locker"},"content":{"rendered":"<h1>Key-Comb Locker<\/h1>\n<h2>1. keypad_scan.sv<\/h2>\n<p>test keypad, with debouncing. <\/p>\n<p>I got two kind of 4&#215;4 keypad, one needs to swap row &amp; column pin sequence, one needn&#8217;t.<\/p>\n<h2>Validation Checklist<\/h2>\n<table>\n<thead>\n<tr>\n<th>Feature<\/th>\n<th>Status<\/th>\n<th>Notes<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Tri-state row control<\/td>\n<td>\u2705 Implemented<\/td>\n<td>Uses <code>generate<\/code> block for clean row drive logic<\/td>\n<\/tr>\n<tr>\n<td>Keypad decoding<\/td>\n<td>\u2705 Implemented<\/td>\n<td>Covers full 4\u00d74 matrix with layout inversion<\/td>\n<\/tr>\n<tr>\n<td>Debounce (1ms sampling)<\/td>\n<td>\u2705 Implemented<\/td>\n<td><code>stable_cnt<\/code> logic at 50MHz<\/td>\n<\/tr>\n<tr>\n<td>Debounce (3-cycle stable)<\/td>\n<td>\u2705 Implemented<\/td>\n<td><code>key_valid_buf<\/code> shift register<\/td>\n<\/tr>\n<tr>\n<td>Valid signal extension<\/td>\n<td>\u2705 Implemented<\/td>\n<td><code>io_valid_shift<\/code> prolongs <code>io[12]<\/code><\/td>\n<\/tr>\n<tr>\n<td>Debug output via LEDs<\/td>\n<td>\u2705 Implemented<\/td>\n<td><code>LEDR[9:5] = io[12:8]<\/code> shows keycode and valid flag<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>keypad_scan.sv<\/h4>\n<pre><code class=\"language-verilog\">module keypad_scan (\n    input  logic        clk,        \/\/ System clock input\n    input  logic        n_rst,      \/\/ Active-low asynchronous reset\n    inout  tri [3:0]    row_out,    \/\/ Row outputs to keypad (tri-state controlled)\n    input  logic [3:0]  col,        \/\/ Column inputs from keypad\n    output logic [12:8] io,         \/\/ Output: io[11:8] = keycode, io[12] = key_valid\n    output logic [9:0]  LEDR        \/\/ Debug LEDs\n);\n\n    \/\/ === Internal Signals ===\n    logic [1:0] row_idx;            \/\/ Current row index for scanning (0\u20133)\n    logic [3:0] row_drive;          \/\/ Active-low row drive control\n    logic [3:0] keycode;            \/\/ Raw decoded key value from keypad\n    logic       key_valid;          \/\/ Indicates a keypress was detected\n\n    \/\/ === Debounce Buffers ===\n    logic [2:0] key_valid_buf;      \/\/ Shift register for key_valid debounce\n    logic [3:0] keycode_buf;        \/\/ Stable keycode output after debounce\n\n    \/\/ === Output Valid Extension ===\n    logic [4:0] io_valid_shift;     \/\/ Extends io[12] signal for multiple cycles\n    assign io[12] = |io_valid_shift; \/\/ Valid if any bit in shift register is high\n    assign io[11:8] = keycode_buf;   \/\/ Output stable keycode\n    assign LEDR[9:5] = io[12:8];     \/\/ Debug: show keycode and valid flag\n\n    \/\/ === Tri-State Row Output Control ===\n    \/\/ Drive one row low at a time, others remain high-Z\n    assign row_out[0] = (row_drive[0]) ? 1&#039;bz : 1&#039;b0;\n    assign row_out[1] = (row_drive[1]) ? 1&#039;bz : 1&#039;b0;\n    assign row_out[2] = (row_drive[2]) ? 1&#039;bz : 1&#039;b0;\n    assign row_out[3] = (row_drive[3]) ? 1&#039;bz : 1&#039;b0;\n\n    \/\/ === Row Scanning Logic ===\n    \/\/ Rotates row index every 1ms to scan keypad\n    always_ff @(posedge clk or negedge n_rst) begin\n        if (!n_rst)\n            row_idx &lt;= 2&#039;d3; \/\/ Start from row 3\n        else if (is_stable)\n            row_idx &lt;= (row_idx == 0) ? 2&#039;d3 : row_idx - 1; \/\/ Rotate rows\n    end\n\n    \/\/ === Row Drive Control ===\n    \/\/ Only one row is actively driven low at a time\n    always_comb begin\n        row_drive = 4&#039;b1111;         \/\/ Default: all rows high-Z\n        row_drive[row_idx] = 1&#039;b0;   \/\/ Drive current row low\n    end\n\n    \/\/ === Key Detection and Decoding ===\n    \/\/ Detects keypress and maps row\/column pair to keycode\n    always_ff @(posedge clk or negedge n_rst) begin\n        if (!n_rst) begin\n            keycode   &lt;= 4&#039;h0;\n            key_valid &lt;= 1&#039;b0;\n        end else if (is_stable) begin\n            key_valid &lt;= 1&#039;b0;\n            for (int j = 3; j &gt;= 0; j--) begin\n                if (!col[j]) begin \/\/ Active-low column detected\n                    \/\/ Decode row\/column pair into keycode\n                    case ({row_idx, j[1:0]})\n                        4&#039;b1111: keycode &lt;= 4&#039;h1;\n                        4&#039;b1110: keycode &lt;= 4&#039;h2;\n                        4&#039;b1101: keycode &lt;= 4&#039;h3;\n                        4&#039;b1100: keycode &lt;= 4&#039;hA;\n                        4&#039;b1011: keycode &lt;= 4&#039;h4;\n                        4&#039;b1010: keycode &lt;= 4&#039;h5;\n                        4&#039;b1001: keycode &lt;= 4&#039;h6;\n                        4&#039;b1000: keycode &lt;= 4&#039;hB;\n                        4&#039;b0111: keycode &lt;= 4&#039;h7;\n                        4&#039;b0110: keycode &lt;= 4&#039;h8;\n                        4&#039;b0101: keycode &lt;= 4&#039;h9;\n                        4&#039;b0100: keycode &lt;= 4&#039;hC;\n                        4&#039;b0011: keycode &lt;= 4&#039;hE;\n                        4&#039;b0010: keycode &lt;= 4&#039;h0;\n                        4&#039;b0001: keycode &lt;= 4&#039;hF;\n                        4&#039;b0000: keycode &lt;= 4&#039;hD;\n                        default: keycode &lt;= 4&#039;h0;\n                    endcase\n                    key_valid &lt;= 1&#039;b1; \/\/ Mark key as valid\n                end\n            end\n        end\n    end\n\n    \/\/ === Debounce Timer ===\n    \/\/ Waits 1ms before sampling key state (50MHz clock \u2192 50,000 cycles)\n    logic [15:0] stable_cnt;\n    logic        is_stable;\n\n    always_ff @(posedge clk or negedge n_rst) begin\n        if (!n_rst) begin\n            stable_cnt &lt;= 16&#039;d0;\n            is_stable  &lt;= 1&#039;b0;\n        end else if (stable_cnt == 16&#039;d49999) begin\n            is_stable  &lt;= 1&#039;b1;  \/\/ 1ms elapsed\n            stable_cnt &lt;= 16&#039;d0;\n        end else begin\n            stable_cnt &lt;= stable_cnt + 1;\n            is_stable  &lt;= 1&#039;b0;\n        end\n    end\n\n    \/\/ === Debounce and Output Latching ===\n    \/\/ Latches keycode only if key_valid is high for 3 consecutive samples\n    always_ff @(posedge clk or negedge n_rst) begin\n        if (!n_rst) begin\n            key_valid_buf  &lt;= 3&#039;b000;\n            keycode_buf    &lt;= 4&#039;h0;\n            io_valid_shift &lt;= 5&#039;b00000;\n        end else begin\n            \/\/ Shift in current key_valid\n            key_valid_buf &lt;= {key_valid_buf[1:0], key_valid};\n\n            \/\/ If key_valid is stable for 3 cycles, latch keycode\n            if (key_valid_buf == 3&#039;b111)\n                keycode_buf &lt;= keycode;\n\n            \/\/ Extend io[12] signal for visibility (5-cycle pulse)\n            io_valid_shift &lt;= {io_valid_shift[3:0], (key_valid_buf == 3&#039;b111)};\n        end\n    end\nendmodule\n<\/code><\/pre>\n<h4>keypad_scan.qsf<\/h4>\n<pre><code class=\"language-tcl\">########## FPGA INFO\nset_global_assignment -name FAMILY &quot;MAX 10&quot;\nset_global_assignment -name DEVICE 10M50DAF484C7G\n\n########## top module\nset_global_assignment -name TOP_LEVEL_ENTITY keypad_scan\n\n########## CLOCK 10MHz ADC, and 2 50MHz clock source\n#set_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to clk_adc\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to clk\n#set_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to clk2\n#set_location_assignment PIN_N5 -to clk_adc\nset_location_assignment PIN_P11 -to clk\nset_location_assignment PIN_N14 -to clk2\n\n########## RESET button &amp; unused button (n_rst, KEY1)\nset_instance_assignment -name IO_STANDARD &quot;3.3 V SCHMITT TRIGGER&quot; -to n_rst\nset_instance_assignment -name IO_STANDARD &quot;3.3 V SCHMITT TRIGGER&quot; -to key1\nset_location_assignment PIN_B8 -to n_rst\nset_location_assignment PIN_A7 -to key1\n\n#===========================================================================\n################### Keypad USed Pins ###########\n### keypad scan row: row[3:0], tri-state output as OC\n# row scan signal out, row[3:0]\uff08tri-state io, set &#039;z&#039; as OC cut-off\uff09\nset_location_assignment PIN_AA12 -to row_out[3]\nset_location_assignment PIN_AA11 -to row_out[2]\nset_location_assignment PIN_Y10 -to row_out[1]\nset_location_assignment PIN_AB9 -to row_out[0]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to row_out[3]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to row_out[2]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to row_out[1]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to row_out[0]\n\n# keypad scan column: col[3:0]\uff08input with internal pull-high\uff09\nset_location_assignment PIN_AB8 -to col[3]\nset_location_assignment PIN_AB7 -to col[2]\nset_location_assignment PIN_AB6 -to col[1]\nset_location_assignment PIN_AB5 -to col[0]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to col[3]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to col[2]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to col[1]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to col[0]\nset_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[3]\nset_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[2]\nset_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[1]\nset_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to col[0]\n\n#### key value output for keyboard\n# io[8]\u2013io[11] as keycode\uff084-bit\uff09\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to io[8]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to io[9]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to io[10]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to io[11]\nset_location_assignment PIN_A11 -to io[8]\nset_location_assignment PIN_B11 -to io[9]\nset_location_assignment PIN_D13 -to io[10]\nset_location_assignment PIN_C13 -to io[11]\n\n# io[12] as key_valid\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to io[12]\nset_location_assignment PIN_E14 -to io[12]\n\n===========================================================================\n### debug LEDS\n# LEDR[0]\u2013LEDR[9] IO_STANDARD &amp; PIN assignment\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[0]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[1]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[2]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[3]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[4]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[5]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[6]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[7]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[8]\nset_instance_assignment -name IO_STANDARD &quot;3.3-V LVTTL&quot; -to LEDR[9]\nset_location_assignment PIN_A8 -to LEDR[0]\nset_location_assignment PIN_A9 -to LEDR[1]\nset_location_assignment PIN_A10 -to LEDR[2]\nset_location_assignment PIN_B10 -to LEDR[3]\nset_location_assignment PIN_D13 -to LEDR[4]\nset_location_assignment PIN_C13 -to LEDR[5]\nset_location_assignment PIN_E14 -to LEDR[6]\nset_location_assignment PIN_D14 -to LEDR[7]\nset_location_assignment PIN_A11 -to LEDR[8]\nset_location_assignment PIN_B11 -to LEDR[9]<\/code><\/pre>\n<h4>keypad_scan.qpf<\/h4>\n<pre><code class=\"language-tcl\">QUARTUS_VERSION = &quot;24.1&quot;\nDATE = &quot;13:15:49  October 11, 2025&quot;\n\n# Revisions\n\nPROJECT_REVISION = &quot;keypad_scan&quot;<\/code><\/pre>\n<h4>keypad_scan.sdc<\/h4>\n<pre><code class=\"language-tcl\">create_clock -name clk -period 20.0 [get_ports clk]<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Key-Comb Locker 1. keypad_scan.sv test keypad, with deb&#8230; &raquo; <a class=\"read-more-link\" href=\"https:\/\/vm1.go2see.me\/?p=1078\">\u95b1\u8b80\u5168\u6587<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,1],"tags":[],"class_list":["post-1078","post","type-post","status-publish","format-standard","hentry","category-21","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts\/1078","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1078"}],"version-history":[{"count":6,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts\/1078\/revisions"}],"predecessor-version":[{"id":1080,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts\/1078\/revisions\/1080"}],"wp:attachment":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1078"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1078"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1078"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}