FabGL
ESP32 Display Controller and Graphics Library
ps2controller.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2021 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6 
7 * Please contact fdivitto2013@gmail.com if you need a commercial license.
8 
9 
10 * This library and related software is available under GPL v3. Feel free to use FabGL in free software and hardware:
11 
12  FabGL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  FabGL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 #include <strings.h>
28 
29 #include "freertos/FreeRTOS.h"
30 
31 #include "esp32/ulp.h"
32 #include "driver/rtc_io.h"
33 #include "soc/sens_reg.h"
34 #if __has_include("soc/rtc_io_periph.h")
35  #include "soc/rtc_io_periph.h"
36 #endif
37 #include "esp_log.h"
38 
39 #include "ps2controller.h"
40 #include "fabutils.h"
41 #include "ulp_macro_ex.h"
42 #include "devdrivers/keyboard.h"
43 #include "devdrivers/mouse.h"
44 
45 
46 
47 #pragma GCC optimize ("O2")
48 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
49 
50 
51 namespace fabgl {
52 
53 
54 
55 
57 // placeholders
58 
59 #define OPCODE_PLACEHOLDER 12 // 12 is an unused ULP opcode we can use as placeholder
60 
61 #define SUB_OPCODE_DAT_ENABLE_OUTPUT 0
62 #define SUB_OPCODE_DAT_ENABLE_INPUT 1
63 #define SUB_OPCODE_CLK_ENABLE_OUTPUT 2
64 #define SUB_OPCODE_CLK_ENABLE_INPUT 3
65 #define SUB_OPCODE_READ_CLK 4
66 #define SUB_OPCODE_READ_DAT 5
67 #define SUB_OPCODE_WRITE_CLK 6
68 #define SUB_OPCODE_WRITE_DAT 7
69 
70 #define PS2_PORT0 0
71 #define PS2_PORT1 1
72 
73 
74 #define DAT_ENABLE_OUTPUT(ps2port, value) { .macro = { \
75  .label = value, \
76  .unused = ps2port, \
77  .sub_opcode = SUB_OPCODE_DAT_ENABLE_OUTPUT, \
78  .opcode = OPCODE_PLACEHOLDER } }
79 
80 #define DAT_ENABLE_INPUT(ps2port, value) { .macro = { \
81  .label = value, \
82  .unused = ps2port, \
83  .sub_opcode = SUB_OPCODE_DAT_ENABLE_INPUT, \
84  .opcode = OPCODE_PLACEHOLDER } }
85 
86 #define CLK_ENABLE_OUTPUT(ps2port, value) { .macro = { \
87  .label = value, \
88  .unused = ps2port, \
89  .sub_opcode = SUB_OPCODE_CLK_ENABLE_OUTPUT, \
90  .opcode = OPCODE_PLACEHOLDER } }
91 
92 #define CLK_ENABLE_INPUT(ps2port, value) { .macro = { \
93  .label = value, \
94  .unused = ps2port, \
95  .sub_opcode = SUB_OPCODE_CLK_ENABLE_INPUT, \
96  .opcode = OPCODE_PLACEHOLDER } }
97 
98 // equivalent to rtc_gpio_get_level()
99 #define READ_CLK(ps2port) { .macro = { \
100  .label = 0, \
101  .unused = ps2port, \
102  .sub_opcode = SUB_OPCODE_READ_CLK, \
103  .opcode = OPCODE_PLACEHOLDER } }
104 
105 // equivalent to rtc_gpio_get_level()
106 #define READ_DAT(ps2port) { .macro = { \
107  .label = 0, \
108  .unused = ps2port, \
109  .sub_opcode = SUB_OPCODE_READ_DAT, \
110  .opcode = OPCODE_PLACEHOLDER } }
111 
112 // equivalent to rtc_gpio_set_level()
113 #define WRITE_CLK(ps2port, value) { .macro = { \
114  .label = value, \
115  .unused = ps2port, \
116  .sub_opcode = SUB_OPCODE_WRITE_CLK, \
117  .opcode = OPCODE_PLACEHOLDER } }
118 
119 // equivalent to rtc_gpio_set_level()
120 #define WRITE_DAT(ps2port, value) { .macro = { \
121  .label = value, \
122  .unused = ps2port, \
123  .sub_opcode = SUB_OPCODE_WRITE_DAT, \
124  .opcode = OPCODE_PLACEHOLDER } }
125 
126 
128 // macro instructions
129 
130 // equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_INPUT_ONLY)
131 #define CONFIGURE_DAT_INPUT(ps2port) \
132  DAT_ENABLE_OUTPUT(ps2port, 0), \
133  DAT_ENABLE_INPUT(ps2port, 1)
134 
135 // equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_OUTPUT_ONLY)
136 #define CONFIGURE_DAT_OUTPUT(ps2port) \
137  DAT_ENABLE_OUTPUT(ps2port, 1), \
138  DAT_ENABLE_INPUT(ps2port, 0)
139 
140 // equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_INPUT_ONLY)
141 #define CONFIGURE_CLK_INPUT(ps2port) \
142  CLK_ENABLE_OUTPUT(ps2port, 0), \
143  CLK_ENABLE_INPUT(ps2port, 1)
144 
145 // equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_OUTPUT_ONLY)
146 #define CONFIGURE_CLK_OUTPUT(ps2port) \
147  CLK_ENABLE_OUTPUT(ps2port, 1), \
148  CLK_ENABLE_INPUT(ps2port, 0)
149 
150 // equivalent to rtc_gpio_set_level()
151 // WRITE bit 0 of R0
152 #define WRITE_DAT_R0(ps2port) \
153  I_BL(3, 1), \
154  WRITE_DAT(ps2port, 1), \
155  I_BGE(2, 1), \
156  WRITE_DAT(ps2port, 0)
157 
158 // performs [addr] = value
159 // changes: R0, R1
160 #define MEM_WRITEI(addr, value) \
161  I_MOVI(R0, addr), \
162  I_MOVI(R1, value), \
163  I_ST(R1, R0, 0)
164 
165 // performs [[addr]] = value
166 // changes: R0, R1
167 #define MEM_INDWRITEI(addr, value) \
168  I_MOVI(R0, addr), \
169  I_LD(R0, R0, 0), \
170  I_MOVI(R1, value), \
171  I_ST(R1, R0, 0)
172 
173 // performs [addr] = reg (reg cannot be R0)
174 // changes R0
175 #define MEM_WRITER(addr, reg) \
176  I_MOVI(R0, addr), \
177  I_ST(reg, R0, 0)
178 
179 // performs [[addr]] = reg (reg cannot be R0)
180 // changes R0
181 #define MEM_INDWRITER(addr, reg) \
182  I_MOVI(R0, addr), \
183  I_LD(R0, R0, 0), \
184  I_ST(reg, R0, 0)
185 
186 // performs reg = [addr]
187 #define MEM_READR(reg, addr) \
188  I_MOVI(reg, addr), \
189  I_LD(reg, reg, 0)
190 
191 // performs reg = [[addr]] (reg cannot be R0)
192 #define MEM_INDREADR(reg, addr) \
193  I_MOVI(reg, addr), \
194  I_LD(reg, reg, 0), \
195  I_LD(reg, reg, 0)
196 
197 // performs [addr] = [addr] + 1
198 // changes: R0, R1
199 #define MEM_INC(addr) \
200  I_MOVI(R0, addr), \
201  I_LD(R1, R0, 0), \
202  I_ADDI(R1, R1, 1), \
203  I_ST(R1, R0, 0)
204 
205 // jump to "label" if [addr] < value
206 // changes: R0
207 #define MEM_BL(label, addr, value) \
208  I_MOVI(R0, addr), \
209  I_LD(R0, R0, 0), \
210  M_BL(label, value)
211 
212 // jump to "label" if [addr] >= value
213 // changes: R0
214 #define MEM_BGE(label, addr, value) \
215  I_MOVI(R0, addr), \
216  I_LD(R0, R0, 0), \
217  M_BGE(label, value)
218 
219 // long jump version of M_BGE
220 #define M_LONG_BGE(label, value) \
221  I_BL(2, value), \
222  M_BX(label)
223 
224 // long jump version of M_BL
225 #define M_LONG_BL(label, value) \
226  I_BGE(2, value), \
227  M_BX(label)
228 
229 // long jump to "label" if R0 == value
230 #define M_LONG_BE(label, value) \
231  I_BL(3, value), \
232  I_BGE(2, value + 1), \
233  M_BX(label)
234 
235 // long jump to "label" if R0 != value
236 #define M_LONG_BNE(label, value) \
237  I_BGE(2, value), \
238  M_BX(label), \
239  I_BL(2, value + 1), \
240  M_BX(label)
241 
242 #define M_LONG_STAGEBLE(label_num, imm_value) \
243  I_STAGEBLE(2, imm_value), \
244  I_STAGEBGE(2, imm_value), \
245  M_BX(label_num)
246 
247 #define M_LONG_STAGEBL(label_num, imm_value) \
248  I_STAGEBGE(2, imm_value), \
249  M_BX(label_num)
250 
251 #define M_LONG_STAGEBGE(label_num, imm_value) \
252  I_STAGEBL(2, imm_value), \
253  M_BX(label_num)
254 
255 // ULP clock is 8MHz, so every cycle takes 0.125us and 1us = 8 cycles
256 #define M_DELAY_US(us) I_DELAY(us * 8)
257 
258 
260 
261 
262 
263 // Locations inside RTC low speed memory
264 
265 #define RTCMEM_PROG_START 0x000 // where the program begins
266 #define RTCMEM_VARS_START 0x200 // where the variables begin
267 
268 // commands (set by CPU, reset by ULP)
269 #define RTCMEM_PORT0_TX (RTCMEM_VARS_START + 0) // 1 = send data to port 0
270 #define RTCMEM_PORT1_TX (RTCMEM_VARS_START + 1) // 1 = send data to port 1
271 #define RTCMEM_PORT0_RX_ENABLE (RTCMEM_VARS_START + 2) // 1 = Request to enable RX from port 0
272 #define RTCMEM_PORT1_RX_ENABLE (RTCMEM_VARS_START + 3) // 1 = Request to enable RX from port 1
273 #define RTCMEM_PORT0_RX_DISABLE (RTCMEM_VARS_START + 4) // 1 = Request to disable RX from port 0
274 #define RTCMEM_PORT1_RX_DISABLE (RTCMEM_VARS_START + 5) // 1 = Request to disable RX from port 1
275 // commands parameters (set by CPU)
276 #define RTCMEM_PORT0_DATAOUT (RTCMEM_VARS_START + 6) // Data to send to port 0 (when RTCMEM_PORT0_TX = 1)
277 #define RTCMEM_PORT1_DATAOUT (RTCMEM_VARS_START + 7) // Data to send to port 1 (when RTCMEM_PORT1_TX = 1)
278 
279 // flags (set by ULP, reset by CPU), generate interrupt
280 #define RTCMEM_PORT0_RX (RTCMEM_VARS_START + 8) // Data received from port 0
281 #define RTCMEM_PORT1_RX (RTCMEM_VARS_START + 9) // Data received from port 1
282 #define RTCMEM_PORT0_RX_CLK_TIMEOUT (RTCMEM_VARS_START + 10) // RX port 0 CLK timeout
283 #define RTCMEM_PORT1_RX_CLK_TIMEOUT (RTCMEM_VARS_START + 11) // RX port 1 CLK timeout
284 // flags parameters (set by ULP)
285 #define RTCMEM_PORT0_DATAIN (RTCMEM_VARS_START + 12) // Data received from port 0 (when RTCMEM_PORT0_RX = 1)
286 #define RTCMEM_PORT1_DATAIN (RTCMEM_VARS_START + 13) // Data received from port 1 (when RTCMEM_PORT1_RX = 1)
287 
288 // internal variables
289 #define RTCMEM_PORT0_RX_ENABLED (RTCMEM_VARS_START + 14) // 0 = port 0 not configured for RX, 1 = port 0 configured for RX
290 #define RTCMEM_PORT1_RX_ENABLED (RTCMEM_VARS_START + 15) // 0 = port 1 not configured for RX, 1 = port 1 configured for RX
291 
292 #define RTCMEM_LASTVAR (RTCMEM_VARS_START + 15)
293 
294 
295 // RX maximum time between CLK cycles (reliable minimum is about 15)
296 #define CLK_RX_TIMEOUT_VAL 100
297 
298 // TX maximum time between CLK cycles
299 #define CLK_TX_TIMEOUT_VAL 1200 // fine tuned to work with PERIBOARD 409P
300 
301 // counter (R2) re-wake value
302 #define WAKE_THRESHOLD 3000
303 
304 
305 // check RTC memory occupation
306 #if RTCMEM_LASTVAR >= 0x800
307 #error "ULP program too big"
308 #endif
309 
310 
311 // ULP program labels
312 
313 #define LABEL_WAIT_COMMAND 0
314 
315 #define LABEL_RX 1
316 #define LABEL_RX_NEXT 2
317 
318 #define LABEL_PORT0_ENABLE_RX 3
319 #define LABEL_PORT0_STOP_RX 4
320 #define LABEL_PORT0_TX 5
321 #define LABEL_PORT0_TX_NEXT_BIT 6
322 #define LABEL_PORT0_TX_WAIT_CLK_HIGH 7
323 #define LABEL_PORT0_TX_EXIT 8
324 #define LABEL_PORT0_RX_WAIT_LOOP 9
325 #define LABEL_PORT0_RX_CLK_IS_HIGH 10
326 #define LABEL_PORT0_RX_CLK_IS_LOW 11
327 #define LABEL_PORT0_RX_CLK_TIMEOUT 12
328 #define LABEL_PORT0_RX_CHECK_CLK 13
329 
330 #define LABEL_PORT1_ENABLE_RX 14
331 #define LABEL_PORT1_STOP_RX 15
332 #define LABEL_PORT1_TX 16
333 #define LABEL_PORT1_TX_NEXT_BIT 17
334 #define LABEL_PORT1_TX_WAIT_CLK_HIGH 18
335 #define LABEL_PORT1_TX_EXIT 19
336 #define LABEL_PORT1_RX_WAIT_LOOP 20
337 #define LABEL_PORT1_RX_CLK_IS_HIGH 21
338 #define LABEL_PORT1_RX_CLK_IS_LOW 22
339 #define LABEL_PORT1_RX_CLK_TIMEOUT 23
340 #define LABEL_PORT1_RX_CHECK_CLK 24
341 
342 
343 // Helpers
344 
345 // temporarily disable port 0
346 // note: before disable check if it was enabled. If it wasn't enabled just exit.
347 // changes: R0
348 #define TEMP_PORT0_DISABLE() \
349  I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLED), \
350  I_BL(4, 1), \
351  CONFIGURE_CLK_OUTPUT(PS2_PORT0), \
352  WRITE_CLK(PS2_PORT0, 0)
353 
354 // temporarily enable port 0
355 // note: before enable check if it was enabled. If it wasn't enabled just exit
356 // changes: R0
357 #define TEMP_PORT0_ENABLE() \
358  I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLED), \
359  I_BL(3, 1), \
360  CONFIGURE_CLK_INPUT(PS2_PORT0)
361 
362 // temporarily disable port 1
363 // note: before disable check if it was enabled. If it wasn't enabled just exit.
364 // changes: R0
365 #define TEMP_PORT1_DISABLE() \
366  I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLED), \
367  I_BL(4, 1), \
368  CONFIGURE_CLK_OUTPUT(PS2_PORT1), \
369  WRITE_CLK(PS2_PORT1, 0)
370 
371 // temporarily enable port 1
372 // note: before enable check if it was enabled. If it wasn't enabled just exit
373 // changes: R0
374 #define TEMP_PORT1_ENABLE() \
375  I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLED), \
376  I_BL(3, 1), \
377  CONFIGURE_CLK_INPUT(PS2_PORT1)
378 
379 // permanently disable port 0
380 // changes: R0
381 #define PERM_PORT0_DISABLE() \
382  I_MOVI(R0, 0), \
383  I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLED), \
384  CONFIGURE_CLK_OUTPUT(PS2_PORT0), \
385  WRITE_CLK(PS2_PORT0, 0)
386 
387 // permanently disable port 1
388 // changes: R0
389 #define PERM_PORT1_DISABLE() \
390  I_MOVI(R0, 0), \
391  I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLED), \
392  CONFIGURE_CLK_OUTPUT(PS2_PORT1), \
393  WRITE_CLK(PS2_PORT1, 0)
394 
395 
396 /*
397  Notes about ULP registers usage
398 
399  R0:
400  General purpose temporary accumulator
401 
402  R1:
403  General purpose temporary register
404 
405  R2:
406  LABEL_PORT0_TX / LABEL_PORT1_TX: Word to send. Reset to 0 on exit.
407  LABEL_RX: Re-wake counter register, when port 0 or port 1 is disabled.
408  General purpose temporary register on receiving data (reset at the end or timeout).
409 
410  R3:
411  Base address for variables (0x0000)
412  LABEL_PORT0_TX / LABEL_PORT1_TX: timeout counter waiting for CLK to be Low
413  LABEL_RX: timeout counter waiting for CLK changes
414 
415  STAGE:
416  RX, TX bit counter
417 
418 */
419 
420 
421 const ulp_insn_t ULP_code[] = {
422 
423  // Stop ULP timer, not necessary because this routine never ends
424  I_END(),
425 
426  // R3 contains just 0x0000, for entire execution, which is the base address of all variabiles load, so it is possible to do
427  // I_LD(R#, R3, RTC_MEM_XX) instead of MEM_READR(), saving one instruction
428  I_MOVI(R3, 0x0000),
429 
430 
432  // Command wait main loop
433 
434 M_LABEL(LABEL_WAIT_COMMAND),
435 
436  // port 0 TX?
437  I_LD(R0, R3, RTCMEM_PORT0_TX),
438  M_BGE(LABEL_PORT0_TX, 1), // yes, jump to LABEL_PORT0_TX
439 
440  // port 0 enable RX?
441  I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLE),
442  M_BGE(LABEL_PORT0_ENABLE_RX, 1), // yes, jump to LABEL_PORT0_ENABLE_RX
443 
444  // port 0 disable RX?
445  I_LD(R0, R3, RTCMEM_PORT0_RX_DISABLE),
446  M_BGE(LABEL_PORT0_STOP_RX, 1), // yes, jump to LABEL_PORT0_STOP_RX
447 
448  // port 1 TX?
449  I_LD(R0, R3, RTCMEM_PORT1_TX),
450  M_BGE(LABEL_PORT1_TX, 1), // yes, jump to LABEL_PORT1_TX
451 
452  // port 1 enable RX?
453  I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLE),
454  M_BGE(LABEL_PORT1_ENABLE_RX, 1), // yes, jump to LABEL_PORT1_ENABLE_RX
455 
456  // port 1 disable RX?
457  I_LD(R0, R3, RTCMEM_PORT1_RX_DISABLE),
458  M_BGE(LABEL_PORT1_STOP_RX, 1), // yes, jump to LABEL_PORT1_STOP_RX
459 
460  // check RX from port 0 or port 1
461  M_BX(LABEL_RX),
462 
463 
465  // LABEL_PORT0_ENABLE_RX - Configure port 0 as RX
466 
467 M_LABEL(LABEL_PORT0_ENABLE_RX),
468 
469  // set RX flag for port 0
470  I_MOVI(R0, 1),
471  I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLED), // [RTCMEM_PORT0_RX_ENABLED] = 1
472 
473  // Configure CLK and DAT as inputs
474  CONFIGURE_CLK_INPUT(PS2_PORT0),
475  CONFIGURE_DAT_INPUT(PS2_PORT0),
476 
477  // Reset command
478  I_MOVI(R0, 0),
479  I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLE), // [RTCMEM_PORT0_RX_ENABLE] = 0
480 
481  M_BX(LABEL_RX),
482 
483 
485  // LABEL_PORT1_ENABLE_RX - Configure port 1 as RX
486 
487 M_LABEL(LABEL_PORT1_ENABLE_RX),
488 
489  // set RX flag for port 1
490  I_MOVI(R0, 1),
491  I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLED), // [RTCMEM_PORT1_RX_ENABLED] = 1
492 
493  // Configure CLK and DAT as inputs
494  CONFIGURE_CLK_INPUT(PS2_PORT1),
495  CONFIGURE_DAT_INPUT(PS2_PORT1),
496 
497  // Reset command
498  I_MOVI(R0, 0),
499  I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLE), // [RTCMEM_PORT1_RX_ENABLE] = 0
500 
501  M_BX(LABEL_RX),
502 
503 
505  // LABEL_PORT0_STOP_RX - Stop port 0 RX (drive CLK low)
506 
507 M_LABEL(LABEL_PORT0_STOP_RX),
508 
509  PERM_PORT0_DISABLE(),
510 
511  // Reset command
512  I_MOVI(R0, 0),
513  I_ST(R0, R3, RTCMEM_PORT0_RX_DISABLE), // [RTCMEM_PORT0_RX_DISABLE] = 0
514 
515  M_BX(LABEL_RX),
516 
517 
519  // LABEL_PORT1_STOP_RX - Stop port 1 RX (drive CLK low)
520 
521 M_LABEL(LABEL_PORT1_STOP_RX),
522 
523  PERM_PORT1_DISABLE(),
524 
525  // Reset command
526  I_MOVI(R0, 0),
527  I_ST(R0, R3, RTCMEM_PORT1_RX_DISABLE), // [RTCMEM_PORT1_RX_DISABLE] = 0
528 
529  M_BX(LABEL_RX),
530 
531 
533  // LABEL_PORT0_TX - Send data
534  // Port 0 is automatically enabled for RX at the end
535 
536 M_LABEL(LABEL_PORT0_TX),
537 
538  // Send the word in RTCMEM_PORT0_DATAOUT
539 
540  // put in R2 the word to send (10 bits: data, parity and stop bit)
541  I_LD(R2, R3, RTCMEM_PORT0_DATAOUT), // R2 = [RTCMEM_PORT0_DATAOUT]
542 
543  // reset the bit counter (STAGE register: 0...7 = data0, 8 = parity, 9 = stop bit)
544  I_STAGERSTI(), // STAGE = 0
545 
546  // disable port 1?
547  TEMP_PORT1_DISABLE(),
548 
549  // maintain CLK and DAT low for 200us
550  CONFIGURE_CLK_OUTPUT(PS2_PORT0),
551  WRITE_CLK(PS2_PORT0, 0),
552  M_DELAY_US(200),
553  CONFIGURE_DAT_OUTPUT(PS2_PORT0),
554  WRITE_DAT(PS2_PORT0, 0),
555 
556  // configure CLK as input
557  CONFIGURE_CLK_INPUT(PS2_PORT0),
558 
559 M_LABEL(LABEL_PORT0_TX_NEXT_BIT),
560 
561  // wait for CLK = LOW
562 
563  // temporarily use R3 (which is 0) as timeout counter
564  I_ADDI(R3, R3, 1),
565  I_MOVR(R0, R3),
566  M_BGE(LABEL_PORT0_TX_EXIT, CLK_TX_TIMEOUT_VAL), // jump to LABEL_PORT0_TX_EXIT on CLK timeout
567 
568  // read CLK
569  READ_CLK(PS2_PORT0), // R0 = CLK
570 
571  // repeat if CLK is high
572  M_BGE(LABEL_PORT0_TX_NEXT_BIT, 1), // jump to LABEL_PORT0_TX_NEXT_BIT if R0 >= 1
573 
574  // R3 is no more timeout counter, but variables base
575  I_MOVI(R3, 0),
576 
577  // bit 10 is the ACK from keyboard, don't send anything, just bypass
578  M_STAGEBGE(LABEL_PORT0_TX_WAIT_CLK_HIGH, 10), // jump to LABEL_PORT0_TX_WAIT_CLK_HIGH if STAGE >= 10
579 
580  // CLK is LOW, we are ready to send the bit (LSB of R0)
581  I_ANDI(R0, R2, 1), // R0 = R2 & 1
582  WRITE_DAT_R0(PS2_PORT0), // DAT = LSB of R0
583 
584 M_LABEL(LABEL_PORT0_TX_WAIT_CLK_HIGH),
585 
586  // Wait for CLK = HIGH
587 
588  // read CLK
589  READ_CLK(PS2_PORT0), // R0 = CLK
590 
591  // repeat if CLK is low
592  M_BL(LABEL_PORT0_TX_WAIT_CLK_HIGH, 1), // jump to LABEL_PORT0_TX_WAIT_CLK_HIGH if R0 < 1
593 
594  // shift the sending word 1 bit to the right (prepare next bit to send)
595  I_RSHI(R2, R2, 1), // R2 = R2 >> 1
596 
597  // increment bit count
598  I_STAGEINCI(1), // STAGE = STAGE + 1
599 
600  // end of word? if not send then another bit
601  M_STAGEBL(LABEL_PORT0_TX_NEXT_BIT, 11), // jump to LABEL_PORT0_TX_NEXT_BIT if STAGE < 11
602 
603 M_LABEL(LABEL_PORT0_TX_EXIT),
604 
605  // R3 is no more timeout counter, but variables base
606  I_MOVI(R3, 0),
607 
608  // back to RX mode
609  CONFIGURE_DAT_INPUT(PS2_PORT0),
610  I_MOVI(R0, 1),
611  I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLED), // [RTCMEM_PORT0_RX_ENABLED] = 1
612 
613  // reset command field
614  I_MOVI(R0, 0),
615  I_ST(R0, R3, RTCMEM_PORT0_TX), // [RTCMEM_PORT0_TX] = 0
616 
617  // re-enable port 1?
618  TEMP_PORT1_ENABLE(),
619 
620  // from now R2 is used as CPU re-wake timeout
621  I_MOVI(R2, 0),
622 
623  // go to command loop
624  M_BX(LABEL_RX),
625 
626 
628  // LABEL_PORT1_TX - Send data
629  // Port 1 is automatically enabled for RX at the end
630 
631 M_LABEL(LABEL_PORT1_TX),
632 
633  // Send the word in RTCMEM_PORT1_DATAOUT
634 
635  // put in R2 the word to send (10 bits: data, parity and stop bit)
636  I_LD(R2, R3, RTCMEM_PORT1_DATAOUT), // R2 = [RTCMEM_PORT1_DATAOUT]
637 
638  // reset the bit counter (STAGE register: 0...7 = data0, 8 = parity, 9 = stop bit)
639  I_STAGERSTI(), // STAGE = 0
640 
641  // disable port 0?
642  TEMP_PORT0_DISABLE(),
643 
644  // maintain CLK and DAT low for 200us
645  CONFIGURE_CLK_OUTPUT(PS2_PORT1),
646  WRITE_CLK(PS2_PORT1, 0),
647  M_DELAY_US(200),
648  CONFIGURE_DAT_OUTPUT(PS2_PORT1),
649  WRITE_DAT(PS2_PORT1, 0),
650 
651  // configure CLK as input
652  CONFIGURE_CLK_INPUT(PS2_PORT1),
653 
654 M_LABEL(LABEL_PORT1_TX_NEXT_BIT),
655 
656  // wait for CLK = LOW
657 
658  // temporarily use R3 (which is 0) as timeout counter
659  I_ADDI(R3, R3, 1),
660  I_MOVR(R0, R3),
661  M_BGE(LABEL_PORT1_TX_EXIT, CLK_TX_TIMEOUT_VAL), // jump to LABEL_PORT1_TX_EXIT on CLK timeout
662 
663  // read CLK
664  READ_CLK(PS2_PORT1), // R0 = CLK
665 
666  // repeat if CLK is high
667  M_BGE(LABEL_PORT1_TX_NEXT_BIT, 1), // jump to LABEL_PORT1_TX_NEXT_BIT if R0 >= 1
668 
669  // R3 is no more timeout counter, but variables base
670  I_MOVI(R3, 0),
671 
672  // bit 10 is the ACK from keyboard, don't send anything, just bypass
673  M_STAGEBGE(LABEL_PORT1_TX_WAIT_CLK_HIGH, 10), // jump to LABEL_PORT1_TX_WAIT_CLK_HIGH if STAGE >= 10
674 
675  // CLK is LOW, we are ready to send the bit (LSB of R0)
676  I_ANDI(R0, R2, 1), // R0 = R2 & 1
677  WRITE_DAT_R0(PS2_PORT1), // DAT = LSB of R0
678 
679 M_LABEL(LABEL_PORT1_TX_WAIT_CLK_HIGH),
680 
681  // Wait for CLK = HIGH
682 
683  // read CLK
684  READ_CLK(PS2_PORT1), // R0 = CLK
685 
686  // repeat if CLK is low
687  M_BL(LABEL_PORT1_TX_WAIT_CLK_HIGH, 1), // jump to LABEL_PORT1_TX_WAIT_CLK_HIGH if R0 < 1
688 
689  // shift the sending word 1 bit to the right (prepare next bit to send)
690  I_RSHI(R2, R2, 1), // R2 = R2 >> 1
691 
692  // increment bit count
693  I_STAGEINCI(1), // STAGE = STAGE + 1
694 
695  // end of word? if not send then another bit
696  M_STAGEBL(LABEL_PORT1_TX_NEXT_BIT, 11), // jump to LABEL_PORT1_TX_NEXT_BIT if STAGE < 11
697 
698 M_LABEL(LABEL_PORT1_TX_EXIT),
699 
700  // R3 is no more timeout counter, but variables base
701  I_MOVI(R3, 0),
702 
703  // back to RX mode
704  CONFIGURE_DAT_INPUT(PS2_PORT1),
705  I_MOVI(R0, 1),
706  I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLED), // [RTCMEM_PORT1_RX_ENABLED] = 1
707 
708  // reset command field
709  I_MOVI(R0, 0),
710  I_ST(R0, R3, RTCMEM_PORT1_TX), // [RTCMEM_PORT0_TX] = 0
711 
712  // re-enable port 0?
713  TEMP_PORT0_ENABLE(),
714 
715  // from now R2 is used as CPU re-wake timeout
716  I_MOVI(R2, 0),
717 
718  // go to command loop
719  M_BX(LABEL_RX),
720 
721 
723  // LABEL_PORT0_RX_CLK_TIMEOUT
724 
725 M_LABEL(LABEL_PORT0_RX_CLK_TIMEOUT),
726 
727  // R3 is no more timeout counter, but variables base
728  I_MOVI(R3, 0),
729 
730  // disable port 0
731  PERM_PORT0_DISABLE(),
732 
733  // re-enable port 1?
734  TEMP_PORT1_ENABLE(),
735 
736  // set port 0 timeout flag
737  I_MOVI(R0, 1),
738  I_ST(R0, R3, RTCMEM_PORT0_RX_CLK_TIMEOUT),
739 
740  I_WAKE(),
741 
742  // from now R2 is used as CPU re-wake timeout
743  I_MOVI(R2, 0),
744 
745  M_BX(LABEL_RX),
746 
747 
749  // LABEL_PORT1_RX_CLK_TIMEOUT
750 
751 M_LABEL(LABEL_PORT1_RX_CLK_TIMEOUT),
752 
753  // R3 is no more timeout counter, but variables base
754  I_MOVI(R3, 0),
755 
756  // disable port 1
757  PERM_PORT1_DISABLE(),
758 
759  // re-enable port 0?
760  TEMP_PORT0_ENABLE(),
761 
762  // set port 1 timeout flag
763  I_MOVI(R0, 1),
764  I_ST(R0, R3, RTCMEM_PORT1_RX_CLK_TIMEOUT),
765 
766  I_WAKE(),
767 
768  // from now R2 is used as CPU re-wake timeout
769  I_MOVI(R2, 0),
770 
771  //M_BX(LABEL_RX),
772 
773 
775  // LABEL_RX - Check for new data from port 0 and port 1
776  // During reception port 1 is disabled for RX. It will be enabled at the end.
777  // After received data the port 0 remains disabled for RX.
778 
779 M_LABEL(LABEL_RX),
780 
781  // Check for RX from port 0?
782  I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLED),
783  M_BGE(LABEL_PORT0_RX_CHECK_CLK, 1), // yes, jump to LABEL_PORT0_RX_CHECK_CLK
784 
785  // no, port 0 is not enabled, maybe we are waiting for an ack from SoC, increment and check the re-wake timeout counter (R2)
786  I_ADDI(R2, R2, 1), // R2 = R2 + 1 (increment counter)
787  I_MOVR(R0, R2),
788  M_BL(LABEL_RX_NEXT, WAKE_THRESHOLD), // check the other port if counter is below WAKE_THRESHOLD
789  I_WAKE(), // need to wake CPU
790  I_MOVI(R2, 0), // reset counter
791  M_BX(LABEL_RX_NEXT), // check the other port
792 
793 M_LABEL(LABEL_PORT0_RX_CHECK_CLK),
794 
795  // read CLK
796  READ_CLK(PS2_PORT0), // R0 = CLK
797 
798  // CLK is low?
799  M_BGE(LABEL_RX_NEXT, 1), // no, jump to LABEL_RX_NEXT
800 
801  // yes, from here we are receiving data from port 0
802 
803  // reset state variables
804  I_MOVI(R0, 0),
805  I_ST(R0, R3, RTCMEM_PORT0_DATAIN), // [RTCMEM_PORT0_DATAIN] = 0
806  I_STAGERSTI(), // STAGE = 0 (STAGE is the bit count)
807  I_MOVI(R2, 0), // R2 = 0 (R2 represents last read value from CLK)
808 
809  // disable port 1?
810  TEMP_PORT1_DISABLE(),
811 
812  // CLK is low so jump directly to DAT capture without recheck
813  M_BX(LABEL_PORT0_RX_CLK_IS_LOW),
814 
815 M_LABEL(LABEL_PORT0_RX_WAIT_LOOP),
816 
817  // temporarily use R3 (which is 0) as timeout counter
818  I_ADDI(R3, R3, 1),
819  I_MOVR(R0, R3),
820  M_BGE(LABEL_PORT0_RX_CLK_TIMEOUT, CLK_RX_TIMEOUT_VAL), // jump to LABEL_PORT0_RX_CLK_TIMEOUT on CLK timeout
821 
822  // read CLK
823  READ_CLK(PS2_PORT0), // R0 = CLK
824 
825  // CLK changed?
826  I_SUBR(R1, R2, R0), // R1 = R2 - R0
827  M_BXZ(LABEL_PORT0_RX_WAIT_LOOP), // no, repeat to LABEL_PORT0_RX_WAIT_LOOP
828 
829  // R3 is no more timeout counter, but variables base
830  I_MOVI(R3, 0),
831 
832  // update last bit received
833  I_MOVR(R2, R0), // R2 = R0
834 
835  // is CLK high?
836  M_BGE(LABEL_PORT0_RX_CLK_IS_HIGH, 1),
837 
838 M_LABEL(LABEL_PORT0_RX_CLK_IS_LOW),
839 
840  // CLK is Low, capture DAT value
841  READ_DAT(PS2_PORT0), // R0 = DAT
842 
843  // merge with data word and shift right by 1 the received word
844  I_LSHI(R0, R0, 11), // R0 = R0 << 11
845  I_LD(R1, R3, RTCMEM_PORT0_DATAIN), // R1 = [RTCMEM_PORT0_DATAIN]
846  I_ORR(R1, R1, R0), // R1 = R1 | R0
847  I_RSHI(R1, R1, 1), // R1 = R1 >> 1
848  I_ST(R1, R3, RTCMEM_PORT0_DATAIN), // [RTCMEM_PORT0_DATAIN] = R1
849 
850  // repeat
851  M_BX(LABEL_PORT0_RX_WAIT_LOOP),
852 
853 M_LABEL(LABEL_PORT0_RX_CLK_IS_HIGH),
854 
855  // increment bit count
856  I_STAGEINCI(1), // STAGE = STAGE + 1
857 
858  // end of word? if not get another bit
859  M_STAGEBL(LABEL_PORT0_RX_WAIT_LOOP, 11), // jump to LABEL_PORT0_RX_WAIT_LOOP if STAGE < 11
860 
861  // End of word, disable port 0
862  PERM_PORT0_DISABLE(),
863 
864  // set RX flag
865  I_MOVI(R0, 1),
866  I_ST(R0, R3, RTCMEM_PORT0_RX), // [RTCMEM_PORT0_RX] = 1
867 
868  // re-enable port 1?
869  TEMP_PORT1_ENABLE(),
870 
871  // don't check RTC_CNTL_RDY_FOR_WAKEUP_S of RTC_CNTL_LOW_POWER_ST_REG because it never gets active, due
872  // the fact ULP never calls HALT or SoC is always active.
873  I_WAKE(),
874 
875  // from now R2 is used as CPU re-wake timeout
876  I_MOVI(R2, 0),
877 
878 
880  // Check for new data from port 1
881  // During reception port 0 is disabled for RX. It will be enabled at the end.
882  // After received data the port 0 remains disabled for RX.
883 
884 M_LABEL(LABEL_RX_NEXT),
885 
886  // Check for RX from port 1?
887  I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLED),
888  M_BGE(LABEL_PORT1_RX_CHECK_CLK, 1), // yes, jump to LABEL_PORT1_RX_CHECK_CLK
889 
890  // no, port 1 is not enabled, maybe we are waiting for an ack from SoC, increment and check the re-wake timeout counter (R2)
891  I_ADDI(R2, R2, 1), // R2 = R2 + 1 (increment counter)
892  I_MOVR(R0, R2),
893  M_LONG_BL(LABEL_WAIT_COMMAND, WAKE_THRESHOLD), // return to main loop if counter is below WAKE_THRESHOLD
894  I_WAKE(), // need to wake CPU
895  I_MOVI(R2, 0), // reset counter
896  M_BX(LABEL_WAIT_COMMAND), // return to main loop
897 
898 M_LABEL(LABEL_PORT1_RX_CHECK_CLK),
899 
900  // read CLK
901  READ_CLK(PS2_PORT1), // R0 = CLK
902 
903  // CLK is low?
904  M_LONG_BGE(LABEL_WAIT_COMMAND, 1), // no, jump to LABEL_WAIT_COMMAND
905 
906  // yes, from here we are receiving data from port 0
907 
908  // reset state variables
909  I_MOVI(R0, 0),
910  I_ST(R0, R3, RTCMEM_PORT1_DATAIN), // [RTCMEM_PORT1_DATAIN] = 0
911  I_STAGERSTI(), // STAGE = 0 (STAGE is the bit count)
912  I_MOVI(R2, 0), // R2 = 0 (R2 represents last read value from CLK)
913 
914  // disable port 0?
915  TEMP_PORT0_DISABLE(),
916 
917  // CLK is low so jump directly to DAT capture without recheck
918  M_BX(LABEL_PORT1_RX_CLK_IS_LOW),
919 
920 M_LABEL(LABEL_PORT1_RX_WAIT_LOOP),
921 
922  // temporarily use R3 (which is 0) as timeout counter
923  I_ADDI(R3, R3, 1),
924  I_MOVR(R0, R3),
925  M_BGE(LABEL_PORT1_RX_CLK_TIMEOUT, CLK_RX_TIMEOUT_VAL), // jump to LABEL_PORT1_RX_CLK_TIMEOUT on CLK timeout
926 
927  // read CLK
928  READ_CLK(PS2_PORT1), // R0 = CLK
929 
930  // CLK changed?
931  I_SUBR(R1, R2, R0), // R1 = R2 - R0
932  M_BXZ(LABEL_PORT1_RX_WAIT_LOOP), // no, repeat to LABEL_PORT1_RX_WAIT_LOOP
933 
934  // R3 is no more timeout counter, but variables base
935  I_MOVI(R3, 0),
936 
937  // update last bit received
938  I_MOVR(R2, R0), // R2 = R0
939 
940  // is CLK high?
941  M_BGE(LABEL_PORT1_RX_CLK_IS_HIGH, 1),
942 
943 M_LABEL(LABEL_PORT1_RX_CLK_IS_LOW),
944 
945  // CLK is Low, capture DAT value
946  READ_DAT(PS2_PORT1), // R0 = DAT
947 
948  // merge with data word and shift right by 1 the received word
949  I_LSHI(R0, R0, 11), // R0 = R0 << 11
950  I_LD(R1, R3, RTCMEM_PORT1_DATAIN), // R1 = [RTCMEM_PORT1_DATAIN]
951  I_ORR(R1, R1, R0), // R1 = R1 | R0
952  I_RSHI(R1, R1, 1), // R1 = R1 >> 1
953  I_ST(R1, R3, RTCMEM_PORT1_DATAIN), // [RTCMEM_PORT1_DATAIN] = R1
954 
955  // repeat
956  M_BX(LABEL_PORT1_RX_WAIT_LOOP),
957 
958 M_LABEL(LABEL_PORT1_RX_CLK_IS_HIGH),
959 
960  // increment bit count
961  I_STAGEINCI(1), // STAGE = STAGE + 1
962 
963  // end of word? if not get another bit
964  M_STAGEBL(LABEL_PORT1_RX_WAIT_LOOP, 11), // jump to LABEL_PORT1_RX_WAIT_LOOP if STAGE < 11
965 
966  // End of word, disable port 1
967  PERM_PORT1_DISABLE(),
968 
969  // set RX flag
970  I_MOVI(R0, 1),
971  I_ST(R0, R3, RTCMEM_PORT1_RX), // [RTCMEM_PORT1_RX] = 1
972 
973  // re-enable port 0?
974  TEMP_PORT0_ENABLE(),
975 
976  // don't check RTC_CNTL_RDY_FOR_WAKEUP_S of RTC_CNTL_LOW_POWER_ST_REG because it never gets active, due
977  // the fact ULP never calls HALT or SoC is always active.
978  I_WAKE(),
979 
980  // from now R2 is used as CPU re-wake timeout
981  I_MOVI(R2, 0),
982 
983  // return to main loop
984  M_BX(LABEL_WAIT_COMMAND),
985 
986 };
987 
988 
989 
990 
991 // Allowed GPIOs: GPIO_NUM_0, GPIO_NUM_2, GPIO_NUM_4, GPIO_NUM_12, GPIO_NUM_13, GPIO_NUM_14, GPIO_NUM_15, GPIO_NUM_25, GPIO_NUM_26, GPIO_NUM_27, GPIO_NUM_32, GPIO_NUM_33
992 // Not allowed from GPIO_NUM_34 to GPIO_NUM_39
993 // prg_start in 32 bit words
994 // size in 32 bit words
995 static void replace_placeholders(uint32_t prg_start, int size, bool port0Enabled, gpio_num_t port0_clkGPIO, gpio_num_t port0_datGPIO, bool port1Enabled, gpio_num_t port1_clkGPIO, gpio_num_t port1_datGPIO)
996 {
997  uint32_t CLK_rtc_gpio_num[2], CLK_rtc_gpio_reg[2], CLK_rtc_gpio_ie_s[2];
998  uint32_t DAT_rtc_gpio_num[2], DAT_rtc_gpio_reg[2], DAT_rtc_gpio_ie_s[2];
999 
1000  if (port0Enabled) {
1001  #if FABGL_ESP_IDF_VERSION <= FABGL_ESP_IDF_VERSION_VAL(3, 3, 5)
1002  CLK_rtc_gpio_num[0] = (uint32_t) rtc_gpio_desc[port0_clkGPIO].rtc_num;
1003  CLK_rtc_gpio_reg[0] = rtc_gpio_desc[port0_clkGPIO].reg;
1004  CLK_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_gpio_desc[port0_clkGPIO].ie) - 1;
1005  DAT_rtc_gpio_num[0] = (uint32_t) rtc_gpio_desc[port0_datGPIO].rtc_num;
1006  DAT_rtc_gpio_reg[0] = rtc_gpio_desc[port0_datGPIO].reg;
1007  DAT_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_gpio_desc[port0_datGPIO].ie) - 1;
1008  #else
1009  int port0_clkIO = rtc_io_number_get(port0_clkGPIO);
1010  CLK_rtc_gpio_num[0] = port0_clkIO;
1011  CLK_rtc_gpio_reg[0] = rtc_io_desc[port0_clkIO].reg;
1012  CLK_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_io_desc[port0_clkIO].ie) - 1;
1013  int port0_datIO = rtc_io_number_get(port0_datGPIO);
1014  DAT_rtc_gpio_num[0] = port0_datIO;
1015  DAT_rtc_gpio_reg[0] = rtc_io_desc[port0_datIO].reg;
1016  DAT_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_io_desc[port0_datIO].ie) - 1;
1017  #endif
1018  }
1019 
1020  if (port1Enabled) {
1021  #if FABGL_ESP_IDF_VERSION <= FABGL_ESP_IDF_VERSION_VAL(3, 3, 5)
1022  CLK_rtc_gpio_num[1] = (uint32_t) rtc_gpio_desc[port1_clkGPIO].rtc_num;
1023  CLK_rtc_gpio_reg[1] = rtc_gpio_desc[port1_clkGPIO].reg;
1024  CLK_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_gpio_desc[port1_clkGPIO].ie) - 1;
1025  DAT_rtc_gpio_num[1] = (uint32_t) rtc_gpio_desc[port1_datGPIO].rtc_num;
1026  DAT_rtc_gpio_reg[1] = rtc_gpio_desc[port1_datGPIO].reg;
1027  DAT_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_gpio_desc[port1_datGPIO].ie) - 1;
1028  #else
1029  int port1_clkIO = rtc_io_number_get(port1_clkGPIO);
1030  CLK_rtc_gpio_num[1] = port1_clkIO;
1031  CLK_rtc_gpio_reg[1] = rtc_io_desc[port1_clkIO].reg;
1032  CLK_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_io_desc[port1_clkIO].ie) - 1;
1033  int port1_datIO = rtc_io_number_get(port1_datGPIO);
1034  DAT_rtc_gpio_num[1] = port1_datIO;
1035  DAT_rtc_gpio_reg[1] = rtc_io_desc[port1_datIO].reg;
1036  DAT_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_io_desc[port1_datIO].ie) - 1;
1037  #endif
1038  }
1039 
1040  for (uint32_t i = 0; i < size; ++i) {
1041  ulp_insn_t * ins = (ulp_insn_t *) RTC_SLOW_MEM + i;
1042  if (ins->macro.opcode == OPCODE_PLACEHOLDER) {
1043  int ps2port = ins->macro.unused;
1044  if ((port0Enabled && ps2port == 0) || (port1Enabled && ps2port == 1)) {
1045  ins->macro.unused = 0;
1046  switch (ins->macro.sub_opcode) {
1047  case SUB_OPCODE_DAT_ENABLE_OUTPUT:
1048  if (ins->macro.label)
1049  *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TS_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TS_S, 1);
1050  else
1051  *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TC_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TC_S, 1);
1052  break;
1053  case SUB_OPCODE_DAT_ENABLE_INPUT:
1054  *ins = (ulp_insn_t) I_WR_REG_BIT(DAT_rtc_gpio_reg[ps2port], DAT_rtc_gpio_ie_s[ps2port], ins->macro.label);
1055  break;
1056  case SUB_OPCODE_CLK_ENABLE_OUTPUT:
1057  if (ins->macro.label)
1058  *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TS_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TS_S, 1);
1059  else
1060  *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TC_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TC_S, 1);
1061  break;
1062  case SUB_OPCODE_CLK_ENABLE_INPUT:
1063  *ins = (ulp_insn_t) I_WR_REG_BIT(CLK_rtc_gpio_reg[ps2port], CLK_rtc_gpio_ie_s[ps2port], ins->macro.label);
1064  break;
1065  case SUB_OPCODE_READ_CLK:
1066  *ins = (ulp_insn_t) I_RD_REG(RTC_GPIO_IN_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S);
1067  break;
1068  case SUB_OPCODE_READ_DAT:
1069  *ins = (ulp_insn_t) I_RD_REG(RTC_GPIO_IN_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S);
1070  break;
1071  case SUB_OPCODE_WRITE_CLK:
1072  *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_OUT_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, ins->macro.label);
1073  break;
1074  case SUB_OPCODE_WRITE_DAT:
1075  *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_OUT_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, ins->macro.label);
1076  break;
1077  }
1078  }
1079  }
1080  }
1081 }
1082 
1083 
1084 PS2Controller * PS2Controller::s_instance = nullptr;
1085 Keyboard * PS2Controller::s_keyboard = nullptr;
1086 Mouse * PS2Controller::s_mouse = nullptr;
1087 bool PS2Controller::s_keyboardAllocated = false;
1088 bool PS2Controller::s_mouseAllocated = false;
1089 bool PS2Controller::s_portEnabled[2];
1090 intr_handle_t PS2Controller::s_ULPWakeISRHandle;
1091 bool PS2Controller::s_parityError[2];
1092 bool PS2Controller::s_syncError[2];
1093 bool PS2Controller::s_CLKTimeOutError[2];
1094 QueueHandle_t PS2Controller::s_dataIn[2];
1095 SemaphoreHandle_t PS2Controller::s_portLock[2];
1096 bool PS2Controller::s_initDone = false;
1097 
1098 
1099 PS2Controller::PS2Controller()
1100 {
1101  if (!s_instance)
1102  s_instance = this;
1103 }
1104 
1105 
1106 PS2Controller::~PS2Controller()
1107 {
1108  end();
1109  if (this == s_instance)
1110  s_instance = nullptr;
1111 }
1112 
1113 
1114 // Note: GPIO_UNUSED is a placeholder used to disable PS/2 port 1.
1115 void PS2Controller::begin(gpio_num_t port0_clkGPIO, gpio_num_t port0_datGPIO, gpio_num_t port1_clkGPIO, gpio_num_t port1_datGPIO)
1116 {
1117  // ULP stuff is always active, even when end() is called
1118  if (!s_initDone) {
1119 
1120  s_portEnabled[0] = (port0_clkGPIO != GPIO_UNUSED && port0_datGPIO != GPIO_UNUSED);
1121  s_portEnabled[1] = (port1_clkGPIO != GPIO_UNUSED && port1_datGPIO != GPIO_UNUSED);
1122 
1123  if (s_portEnabled[0]) {
1124  if (!rtc_gpio_is_valid_gpio(port0_clkGPIO) || !rtc_gpio_is_valid_gpio(port0_datGPIO)) {
1125  ESP_LOGE("FabGL", "Invalid PS/2 Port 0 pins");
1126  s_portEnabled[0] = false;
1127  } else {
1128  rtc_gpio_init(port0_clkGPIO);
1129  rtc_gpio_init(port0_datGPIO);
1130  }
1131  }
1132 
1133  if (s_portEnabled[1]) {
1134  if (!rtc_gpio_is_valid_gpio(port1_clkGPIO) || !rtc_gpio_is_valid_gpio(port1_datGPIO)) {
1135  ESP_LOGE("FabGL", "Invalid PS/2 Port 1 pins");
1136  s_portEnabled[1] = false;
1137  } else {
1138  rtc_gpio_init(port1_clkGPIO);
1139  rtc_gpio_init(port1_datGPIO);
1140  }
1141  }
1142 
1143  // clear ULP memory (without this may fail to run ULP on softreset)
1144  for (int i = RTCMEM_PROG_START; i < RTCMEM_LASTVAR; ++i)
1145  RTC_SLOW_MEM[i] = 0x0000;
1146 
1147  // process, load and execute ULP program
1148  size_t size = sizeof(ULP_code) / sizeof(ulp_insn_t);
1149  ulp_process_macros_and_load_ex(RTCMEM_PROG_START, ULP_code, &size); // convert macros to ULP code
1150  replace_placeholders(RTCMEM_PROG_START, size, s_portEnabled[0], port0_clkGPIO, port0_datGPIO, s_portEnabled[1], port1_clkGPIO, port1_datGPIO); // replace GPIO placeholders
1151  //printf("prog size = %d\n", size);
1152  assert(size < RTCMEM_VARS_START && "ULP Program too long, increase RTCMEM_VARS_START");
1153 
1154  REG_SET_FIELD(SENS_SAR_START_FORCE_REG, SENS_PC_INIT, RTCMEM_PROG_START); // set entry point
1155  SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP); // enable FORCE START
1156 
1157  for (int p = 0; p < 2; ++p) {
1158  RTC_SLOW_MEM[RTCMEM_PORT0_TX + p] = 0;
1159  RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + p] = 0;
1160  RTC_SLOW_MEM[RTCMEM_PORT0_RX_DISABLE + p] = 0;
1161  RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] = 0;
1162  RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLED + p] = 0;
1163  s_parityError[p] = s_syncError[p] = false;
1164  s_dataIn[p] = (s_portEnabled[p] ? xQueueCreate(1, sizeof(uint16_t)) : nullptr);
1165  s_portLock[p] = (s_portEnabled[p] ? xSemaphoreCreateRecursiveMutex() : nullptr);
1166  enableRX(p);
1167  }
1168 
1169  // ULP start
1170  SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP);
1171 
1172  // install RTC interrupt handler (on ULP Wake() instruction)
1173  // note about ESP_INTR_FLAG_LEVEL2: this is necessary in order to work reliably with interrupt intensive VGATextController, when running on the same core
1174  // On some boards only core "1" can -read- RTC slow memory and receive interrupts, hence we have to force core 1.
1175  esp_intr_alloc_pinnedToCore(ETS_RTC_CORE_INTR_SOURCE, ESP_INTR_FLAG_LEVEL2, ULPWakeISR, nullptr, &s_ULPWakeISRHandle, 1);
1176  SET_PERI_REG_MASK(RTC_CNTL_INT_ENA_REG, RTC_CNTL_ULP_CP_INT_ENA);
1177 
1178  s_initDone = true;
1179 
1180  } else {
1181 
1182  // ULP already initialized
1183  for (int p = 0; p < 2; ++p) {
1184  RTC_SLOW_MEM[RTCMEM_PORT0_TX + p] = 0;
1185  RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + p] = 0;
1186  RTC_SLOW_MEM[RTCMEM_PORT0_RX_DISABLE + p] = 0;
1187  RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] = 0;
1188  RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLED + p] = 0;
1189  s_parityError[p] = s_syncError[p] = false;
1190  if (s_portEnabled[p]) {
1191  xQueueReset(s_dataIn[p]);
1192  xSemaphoreGiveRecursive(s_portLock[p]);
1193  enableRX(p);
1194  }
1195  }
1196  }
1197 }
1198 
1199 
1200 void PS2Controller::begin(PS2Preset preset, KbdMode keyboardMode)
1201 {
1202  end();
1203 
1204  bool generateVirtualKeys = (keyboardMode == KbdMode::GenerateVirtualKeys || keyboardMode == KbdMode::CreateVirtualKeysQueue);
1205  bool createVKQueue = (keyboardMode == KbdMode::CreateVirtualKeysQueue);
1206  switch (preset) {
1208  // both keyboard (port 0) and mouse configured (port 1)
1209  begin(GPIO_NUM_33, GPIO_NUM_32, GPIO_NUM_26, GPIO_NUM_27);
1210  setKeyboard(new Keyboard);
1211  keyboard()->begin(generateVirtualKeys, createVKQueue, 0);
1212  setMouse(new Mouse);
1213  mouse()->begin(1);
1214  s_keyboardAllocated = s_mouseAllocated = true;
1215  break;
1217  // both keyboard (port 1) and mouse configured (port 0)
1218  begin(GPIO_NUM_33, GPIO_NUM_32, GPIO_NUM_26, GPIO_NUM_27);
1219  setMouse(new Mouse);
1220  mouse()->begin(0);
1221  setKeyboard(new Keyboard);
1222  keyboard()->begin(generateVirtualKeys, createVKQueue, 1);
1223  s_keyboardAllocated = s_mouseAllocated = true;
1224  break;
1226  // only keyboard configured on port 0
1227  // this will call setKeyboard and begin()
1228  (new Keyboard)->begin(GPIO_NUM_33, GPIO_NUM_32, generateVirtualKeys, createVKQueue);
1229  s_keyboardAllocated = true;
1230  break;
1232  // only keyboard configured on port 1
1233  // this will call setKeyboard and begin()
1234  (new Keyboard)->begin(GPIO_NUM_26, GPIO_NUM_27, generateVirtualKeys, createVKQueue);
1235  s_keyboardAllocated = true;
1236  break;
1237  case PS2Preset::MousePort0:
1238  // only mouse configured on port 0
1239  // this will call setMouse and begin()
1240  (new Mouse)->begin(GPIO_NUM_33, GPIO_NUM_32);
1241  s_mouseAllocated = true;
1242  break;
1243  case PS2Preset::MousePort1:
1244  // only mouse configured on port 1
1245  // this will call setMouse and begin()
1246  (new Mouse)->begin(GPIO_NUM_26, GPIO_NUM_27);
1247  s_mouseAllocated = true;
1248  break;
1249  };
1250 }
1251 
1252 
1253 void PS2Controller::end()
1254 {
1255  if (s_initDone) {
1256  if (s_keyboardAllocated)
1257  delete s_keyboard;
1258  s_keyboard = nullptr;
1259 
1260  if (s_mouseAllocated)
1261  delete s_mouse;
1262  s_mouse = nullptr;
1263 
1264  for (int p = 0; p < 2; ++p)
1265  disableRX(p);
1266  }
1267 }
1268 
1269 
1270 void PS2Controller::disableRX(int PS2Port)
1271 {
1272  if (s_portEnabled[PS2Port])
1273  RTC_SLOW_MEM[RTCMEM_PORT0_RX_DISABLE + PS2Port] = 1;
1274 }
1275 
1276 
1277 void PS2Controller::enableRX(int PS2Port)
1278 {
1279  if (s_portEnabled[PS2Port]) {
1280  // enable RX only if there is not data waiting
1281  if (!dataAvailable(PS2Port))
1282  RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + PS2Port] = 1;
1283  }
1284 }
1285 
1286 
1288 {
1289  return uxQueueMessagesWaiting(s_dataIn[PS2Port]);
1290 }
1291 
1292 
1293 // return -1 when no data is available
1294 int PS2Controller::getData(int PS2Port, int timeOutMS)
1295 {
1296  int r = -1;
1297 
1298  uint16_t w;
1299  if (xQueueReceive(s_dataIn[PS2Port], &w, msToTicks(timeOutMS))) {
1300 
1301  // check CLK timeout, parity, start and stop bits
1302  s_CLKTimeOutError[PS2Port] = (w == 0xffff);
1303  if (!s_CLKTimeOutError[PS2Port]) {
1304  uint8_t startBit = w & 1;
1305  uint8_t stopBit = (w >> 10) & 1;
1306  uint8_t parity = (w >> 9) & 1;
1307  r = (w >> 1) & 0xff;
1308  s_parityError[PS2Port] = (parity != !calcParity(r));
1309  s_syncError[PS2Port] = (startBit != 0 || stopBit != 1);
1310  if (s_parityError[PS2Port] || s_syncError[PS2Port])
1311  r = -1;
1312  }
1313 
1314  // ULP leaves RX disables whenever receives data or CLK timeout, so we need to enable it here
1315  RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + PS2Port] = 1;
1316 
1317  }
1318 
1319  return r;
1320 }
1321 
1322 
1323 void PS2Controller::sendData(uint8_t data, int PS2Port)
1324 {
1325  if (s_portEnabled[PS2Port]) {
1326  RTC_SLOW_MEM[RTCMEM_PORT0_DATAOUT + PS2Port] = 0x200 | ((!calcParity(data) & 1) << 8) | data; // 0x200 = stop bit. Start bit is not specified here.
1327  RTC_SLOW_MEM[RTCMEM_PORT0_TX + PS2Port] = 1;
1328  }
1329 }
1330 
1331 
1332 bool PS2Controller::lock(int PS2Port, int timeOutMS)
1333 {
1334  return s_portEnabled[PS2Port] ? xSemaphoreTakeRecursive(s_portLock[PS2Port], msToTicks(timeOutMS)) : true;
1335 }
1336 
1337 
1338 void PS2Controller::unlock(int PS2Port)
1339 {
1340  if (s_portEnabled[PS2Port])
1341  xSemaphoreGiveRecursive(s_portLock[PS2Port]);
1342 }
1343 
1344 
1345 
1346 void IRAM_ATTR PS2Controller::ULPWakeISR(void * arg)
1347 {
1348  uint32_t rtc_intr = READ_PERI_REG(RTC_CNTL_INT_ST_REG);
1349 
1350  if (rtc_intr & RTC_CNTL_SAR_INT_ST) {
1351 
1352  for (int p = 0; p < 2; ++p) {
1353  if (RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] & 0xffff) {
1354  // RX
1355  uint16_t d = RTC_SLOW_MEM[RTCMEM_PORT0_DATAIN + p] & 0xffff;
1356  xQueueOverwriteFromISR(PS2Controller::s_dataIn[p], &d, nullptr);
1357  RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] = 0;
1358  } else if (RTC_SLOW_MEM[RTCMEM_PORT0_RX_CLK_TIMEOUT + p] & 0xffff) {
1359  // CLK timeout
1360  uint16_t d = 0xffff;
1361  xQueueOverwriteFromISR(PS2Controller::s_dataIn[p], &d, nullptr);
1362  RTC_SLOW_MEM[RTCMEM_PORT0_RX_CLK_TIMEOUT + p] = 0;
1363  }
1364  }
1365 
1366  }
1367 
1368  // clear interrupt
1369  WRITE_PERI_REG(RTC_CNTL_INT_CLR_REG, rtc_intr);
1370 }
1371 
1372 
1373 } // end of namespace
1374 
1375 
This file contains fabgl::PS2Controller definition.
This file contains fabgl::Keyboard definition.
uint8_t const * data
static bool lock(int PS2Port, int timeOutMS)
Gets exclusive access to the specified PS/2 port.
static Mouse * mouse()
Returns the instance of Mouse object automatically created by PS2Controller.
The PS2 Keyboard controller class.
Definition: keyboard.h:77
This file contains fabgl::Mouse definition.
static bool dataAvailable(int PS2Port)
Determines if one byte has been received from the specified port.
static void sendData(uint8_t data, int PS2Port)
Sends a command to the device.
static int getData(int PS2Port, int timeOutMS)
Gets a scancode from the queue.
void begin(gpio_num_t clkGPIO, gpio_num_t dataGPIO, bool generateVirtualKeys=true, bool createVKQueue=true)
Initializes Keyboard specifying CLOCK and DATA GPIOs.
Definition: keyboard.cpp:101
static void unlock(int PS2Port)
Releases port from exclusive access.
This file contains some utility classes and functions.
Definition: canvas.cpp:36
KbdMode
This enum defines how handle keyboard virtual keys.
Definition: ps2controller.h:65
static Keyboard * keyboard()
Returns the instance of Keyboard object automatically created by PS2Controller.
static void disableRX(int PS2Port)
Disables inputs from PS/2 port driving the CLK line Low.
void begin(gpio_num_t clkGPIO, gpio_num_t dataGPIO)
Initializes Mouse specifying CLOCK and DATA GPIOs.
Definition: mouse.cpp:83
static void enableRX(int PS2Port)
Enables inputs from PS/2 port releasing CLK line.
PS2Preset
This enum defines what is connected to PS/2 ports.
Definition: ps2controller.h:52
The PS2 Mouse controller class.
Definition: mouse.h:111
static void begin(gpio_num_t port0_clkGPIO, gpio_num_t port0_datGPIO, gpio_num_t port1_clkGPIO=GPIO_UNUSED, gpio_num_t port1_datGPIO=GPIO_UNUSED)
Initializes PS2 device controller.