§ Контроллер

Здесь приведен код универсального контроллера клавиатуры с выдачей ASCII-символов.
1reg [7:0] kb; // ASCII-код принятого символа
2
3// Подключение модуля
4kb KBD
5(
6    .clock      (clock_25),
7    .ps_clock   (PS2_CLK),
8    .ps_data    (PS2_DAT),
9    .done       (kb_done),
10    .data       (kb_data)
11);
12
13// Получение скан-кода с клавиатуры
14always @(posedge clock_25) if (kb_done) begin kb <= kb_data; end

§ Код

Дата редакции: 16 янв 2024
1module kb
2(
3    input clock,
4    input ps_clock,
5    input ps_data,
6
7    output reg       done,
8    output reg [7:0] data
9);
10
11reg [1:0]   klatch   = 2'b00;
12reg [3:0]   kcount   = 1'b0;
13reg [9:0]   kin      = 1'b0;
14reg [19:0]  kout     = 1'b0;
15reg         kbusy, kdone, released, shift, extended;
16
17// Для Icarus Verilog
18initial begin
19
20    data        = 8'h00;
21    shift       = 1'b0;
22    kbusy       = 1'b0;
23    kdone       = 1'b0;
24    done        = 1'b0;
25    shift       = 1'b0;
26    released    = 1'b0;
27    extended    = 1'b0;
28
29end
30
31// Для повышения точности
32always @(negedge clock) done <= kdone;
33
34// Основная логика работы контроллера
35always @(posedge clock) begin
36
37    kdone <= 1'b0;
38
39    // Процесс приема сигнала
40    if (kbusy) begin
41
42        // Позитивный фронт
43        if (klatch == 2'b01) begin
44
45            // Завершающий такт
46            if (kcount == 4'hA) begin
47
48                kbusy <= 1'b0;
49                data  <= 1'b0;
50
51                // Четность данных должна совпасть
52                if (^kin[9:1]) begin
53
54                    // Клавиша отпущена
55                    if      (kin[8:1] == 8'hF0) begin released <= 1'b1; end
56                    else if (kin[8:1] == 8'hE0) begin extended <= 1'b1; end
57                    // Декодирование клавиши
58                    else begin
59
60                        // Отправка клавиши только при KeyDown
61                        kdone <= ~released;
62
63                        // Декодирование клавиши
64                        case ({extended, kin[8:1]})
65
66                            // Левый и правый SHIFT равнозначны
67                            /* SH */ 8'h12, 8'h59: begin shift <= ~released; kdone <= 1'b0; end
68
69                            // Цифробуквенная клавиатура
70                            /* Aa */ 8'h1C: data <= shift ? 8'h41 : 8'h61;
71                            /* Bb */ 8'h32: data <= shift ? 8'h42 : 8'h62;
72                            /* Cc */ 8'h21: data <= shift ? 8'h43 : 8'h63;
73                            /* Dd */ 8'h23: data <= shift ? 8'h44 : 8'h64;
74                            /* Ee */ 8'h24: data <= shift ? 8'h45 : 8'h65;
75                            /* Ff */ 8'h2B: data <= shift ? 8'h46 : 8'h66;
76                            /* Gg */ 8'h34: data <= shift ? 8'h47 : 8'h67;
77                            /* Hh */ 8'h33: data <= shift ? 8'h48 : 8'h68;
78                            /* Ii */ 8'h43: data <= shift ? 8'h49 : 8'h69;
79                            /* Jj */ 8'h3B: data <= shift ? 8'h4A : 8'h6A;
80                            /* Kk */ 8'h42: data <= shift ? 8'h4B : 8'h6B;
81                            /* Ll */ 8'h4B: data <= shift ? 8'h4C : 8'h6C;
82                            /* Mm */ 8'h3A: data <= shift ? 8'h4D : 8'h6D;
83                            /* Nn */ 8'h31: data <= shift ? 8'h4E : 8'h6E;
84                            /* Oo */ 8'h44: data <= shift ? 8'h4F : 8'h6F;
85                            /* Pp */ 8'h4D: data <= shift ? 8'h50 : 8'h70;
86                            /* Qq */ 8'h15: data <= shift ? 8'h51 : 8'h71;
87                            /* Rr */ 8'h2D: data <= shift ? 8'h52 : 8'h72;
88                            /* Ss */ 8'h1B: data <= shift ? 8'h53 : 8'h73;
89                            /* Tt */ 8'h2C: data <= shift ? 8'h54 : 8'h74;
90                            /* Uu */ 8'h3C: data <= shift ? 8'h55 : 8'h75;
91                            /* Vv */ 8'h2A: data <= shift ? 8'h56 : 8'h76;
92                            /* Ww */ 8'h1D: data <= shift ? 8'h57 : 8'h77;
93                            /* Xx */ 8'h22: data <= shift ? 8'h58 : 8'h78;
94                            /* Yy */ 8'h35: data <= shift ? 8'h59 : 8'h79;
95                            /* Zz */ 8'h1A: data <= shift ? 8'h5A : 8'h7A;
96
97                            // Цифры
98                            /* 0) */ 8'h45: data <= shift ? 8'h29 : 8'h30;
99                            /* 1! */ 8'h16: data <= shift ? 8'h21 : 8'h31;
100                            /* 2@ */ 8'h1E: data <= shift ? 8'h40 : 8'h32;
101                            /* 3# */ 8'h26: data <= shift ? 8'h23 : 8'h33;
102                            /* 4$ */ 8'h25: data <= shift ? 8'h24 : 8'h34;
103                            /* 5% */ 8'h2E: data <= shift ? 8'h25 : 8'h35;
104                            /* 6^ */ 8'h36: data <= shift ? 8'h5E : 8'h36;
105                            /* 7& */ 8'h3D: data <= shift ? 8'h26 : 8'h37;
106                            /* 8* */ 8'h3E: data <= shift ? 8'h2A : 8'h38;
107                            /* 9( */ 8'h46: data <= shift ? 8'h28 : 8'h39;
108
109                            // Спецсимволы
110                            /* `~ */ 8'h0E: data <= shift ? 8'h7E : 8'h60;
111                            /* -_ */ 8'h4E: data <= shift ? 8'h5F : 8'h2D;
112                            /* =+ */ 8'h55: data <= shift ? 8'h2B : 8'h3D;
113                            /* \| */ 8'h5D: data <= shift ? 8'h7C : 8'h5C;
114                            /* [{ */ 8'h54: data <= shift ? 8'h7B : 8'h5B;
115                            /* ]} */ 8'h5B: data <= shift ? 8'h7D : 8'h5D;
116                            /* ;: */ 8'h4C: data <= shift ? 8'h3A : 8'h3B;
117                            /* '" */ 8'h52: data <= shift ? 8'h22 : 8'h27;
118                            /* ,< */ 8'h41: data <= shift ? 8'h3C : 8'h2C;
119                            /* .> */ 8'h49: data <= shift ? 8'h3E : 8'h2E;
120                            /* /? */ 8'h4A: data <= shift ? 8'h3F : 8'h2F;
121
122                            // Разные клавиши
123                            /* BACK */ 8'h66: data <= 8'h08;
124                            /* TAB  */ 8'h0D: data <= 8'h09;
125                            /* ENT  */ 8'h5A: data <= 8'h0A;
126                            /* ESC  */ 8'h76: data <= 8'h1B;
127                            /* SPC  */ 8'h29: data <= 8'h20;
128
129                            // Дополненный набор
130                            /* PGUP */ 9'h17D: data <= 8'h01;
131                            /* PGDN */ 9'h17A: data <= 8'h02;
132                            /* UP   */ 9'h175: data <= 8'h03;
133                            /* RT   */ 9'h174: data <= 8'h04;
134                            /* DN   */ 9'h172: data <= 8'h05;
135                            /* LF   */ 9'h16B: data <= 8'h06;
136                            /* DEL  */ 9'h171: data <= 8'h07;
137                            /* INS  */ 9'h16C: data <= 8'h0B;
138                            /* HOME */ 9'h170: data <= 8'h0C;
139                            /* END  */ 9'h169: data <= 8'h0D;
140
141                            // Клавиши, которые не играют никакой роли
142                            default: kdone <= 1'b0;
143
144                        endcase
145
146                        released <= 1'b0;
147                        extended <= 1'b0;
148
149                    end
150
151                end
152            end
153
154            kcount  <= kcount + 1'b1;
155            kin     <= {ps_data, kin[9:1]};
156
157        end
158
159        // Считать "зависший процесс"
160        kout <= ps_clock ? kout + 1 : 1'b0;
161
162        // И если прошло более 20 мс, то перевести в состояние ожидания
163        if (kout > 25000*20) kbusy <= 1'b0;
164
165    end else begin
166
167        // Обнаружен негативный фронт \__
168        if (klatch == 2'b10) begin
169
170            kbusy   <= 1'b1;    // Активировать прием данных
171            kcount  <= 1'b0;
172            kout    <= 1'b0;
173
174        end
175
176    end
177
178    klatch <= {klatch[0], ps_clock};
179
180end
181
182endmodule