Skip to content

Commit 014f87f

Browse files
Convert state machine to 1-hot for faster lookup
GCC won't use a lookup table for the TWI state machine, so it ends up using a series of straight line compare-jump, compare-jumps to figure out which branch of code to execute for each state. For branches that have multiple states that call them, this can expand to a lot of code. Short-circuit the whole thing by converting the FSM to a 1-hot encoding while executing it, and then just and-ing the 1-hot state with the bitmask of states with the same code. Sketch uses 270719 bytes (25%) of program storage space. Maximum is 1044464 bytes. Global variables use 27944 bytes (34%) of dynamic memory, leaving 53976 bytes for local variables. Maximum is 81920 bytes. 401000cc l F .text1 00000014 twi_delay 401000f4 w F .text1 0000003b twi_releaseBus 4010015c g F .text1 00000246 twi_onTwipEvent 401003c0 l F .text1 000001b1 onSdaChange 40100580 l F .text1 000002da onSclChange 4010085c l F .text1 0000003b onTimer 0x00000000401073cc _text_end = ABSOLUTE (.) Saves 228 bytes of IRAM vs. master, uses 32 additional bytes of heap.
1 parent 62f9599 commit 014f87f

File tree

1 file changed

+107
-145
lines changed

1 file changed

+107
-145
lines changed

cores/esp8266/core_esp8266_si2c.cpp

Lines changed: 107 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,14 @@ static void eventTask(ETSEvent *e)
522522
}
523523
}
524524

525+
// The state machine is converted from a 0...15 state to a 1-hot encoded state, and then
526+
// compared to the logical-or of all states with the same branch. This removes the need
527+
// for a large series of straight-line compares. The biggest win is when multiple states
528+
// all have the same branch (onSdaChange), but for others there is some benefit, still.
529+
#define S2M(x) (1<<(x))
530+
// Shorthand for if the state is any of the or'd bitmask x
531+
#define IFSTATE(x) if (twip_state_mask & (x))
532+
525533
void ICACHE_RAM_ATTR onSclChange(void)
526534
{
527535
unsigned int sda;
@@ -532,151 +540,124 @@ void ICACHE_RAM_ATTR onSclChange(void)
532540

533541
twip_status = 0xF8; // reset TWI status
534542

535-
switch (twip_state)
536-
{
537-
case TWIP_IDLE:
538-
case TWIP_WAIT_STOP:
539-
case TWIP_BUS_ERR:
543+
int twip_state_mask = S2M(twip_state);
544+
IFSTATE(S2M(TWIP_START)|S2M(TWIP_REP_START)|S2M(TWIP_SLA_W)|S2M(TWIP_READ)) {
545+
if (!scl) {
540546
// ignore
541-
break;
547+
} else {
548+
bitCount--;
549+
twi_data <<= 1;
550+
twi_data |= sda;
542551

543-
case TWIP_START:
544-
case TWIP_REP_START:
545-
case TWIP_SLA_W:
546-
case TWIP_READ:
547-
if (!scl) {
548-
// ignore
552+
if (bitCount != 0) {
553+
// continue
549554
} else {
550-
bitCount--;
551-
twi_data <<= 1;
552-
twi_data |= sda;
553-
554-
if (bitCount != 0) {
555-
// continue
555+
twip_state = TWIP_SEND_ACK;
556+
}
557+
}
558+
} else IFSTATE(S2M(TWIP_SEND_ACK)) {
559+
if (scl) {
560+
// ignore
561+
} else {
562+
if (twip_mode == TWIPM_IDLE) {
563+
if ((twi_data & 0xFE) != twi_addr) {
564+
// ignore
556565
} else {
557-
twip_state = TWIP_SEND_ACK;
566+
SDA_LOW();
558567
}
559-
}
560-
break;
561-
562-
case TWIP_SEND_ACK:
563-
if (scl) {
564-
// ignore
565568
} else {
566-
if (twip_mode == TWIPM_IDLE) {
567-
if ((twi_data & 0xFE) != twi_addr) {
568-
// ignore
569-
} else {
570-
SDA_LOW();
571-
}
569+
if (!twi_ack) {
570+
// ignore
572571
} else {
573-
if (!twi_ack) {
574-
// ignore
575-
} else {
576-
SDA_LOW();
577-
}
572+
SDA_LOW();
578573
}
579-
twip_state = TWIP_WAIT_ACK;
580574
}
581-
break;
582-
583-
case TWIP_WAIT_ACK:
584-
if (scl) {
585-
// ignore
586-
} else {
587-
if (twip_mode == TWIPM_IDLE) {
588-
if ((twi_data & 0xFE) != twi_addr) {
589-
SDA_HIGH();
590-
twip_state = TWIP_WAIT_STOP;
591-
} else {
592-
SCL_LOW(); // clock stretching
593-
SDA_HIGH();
594-
twip_mode = TWIPM_ADDRESSED;
595-
if (!(twi_data & 0x01)) {
596-
twip_status = TW_SR_SLA_ACK;
597-
twi_onTwipEvent(twip_status);
598-
bitCount = 8;
599-
twip_state = TWIP_SLA_W;
600-
} else {
601-
twip_status = TW_ST_SLA_ACK;
602-
twi_onTwipEvent(twip_status);
603-
twip_state = TWIP_SLA_R;
604-
}
605-
}
575+
twip_state = TWIP_WAIT_ACK;
576+
}
577+
} else IFSTATE(S2M(TWIP_WAIT_ACK)) {
578+
if (scl) {
579+
// ignore
580+
} else {
581+
if (twip_mode == TWIPM_IDLE) {
582+
if ((twi_data & 0xFE) != twi_addr) {
583+
SDA_HIGH();
584+
twip_state = TWIP_WAIT_STOP;
606585
} else {
607586
SCL_LOW(); // clock stretching
608587
SDA_HIGH();
609-
if (!twi_ack) {
610-
twip_status = TW_SR_DATA_NACK;
588+
twip_mode = TWIPM_ADDRESSED;
589+
if (!(twi_data & 0x01)) {
590+
twip_status = TW_SR_SLA_ACK;
611591
twi_onTwipEvent(twip_status);
612-
twip_mode = TWIPM_WAIT;
613-
twip_state = TWIP_WAIT_STOP;
592+
bitCount = 8;
593+
twip_state = TWIP_SLA_W;
614594
} else {
615-
twip_status = TW_SR_DATA_ACK;
595+
twip_status = TW_ST_SLA_ACK;
616596
twi_onTwipEvent(twip_status);
617-
bitCount = 8;
618-
twip_state = TWIP_READ;
597+
twip_state = TWIP_SLA_R;
619598
}
620599
}
621-
}
622-
break;
623-
624-
case TWIP_SLA_R:
625-
case TWIP_WRITE:
626-
if (scl) {
627-
// ignore
628600
} else {
629-
bitCount--;
630-
(twi_data & 0x80) ? SDA_HIGH() : SDA_LOW();
631-
twi_data <<= 1;
632-
633-
if (bitCount != 0) {
634-
// continue
601+
SCL_LOW(); // clock stretching
602+
SDA_HIGH();
603+
if (!twi_ack) {
604+
twip_status = TW_SR_DATA_NACK;
605+
twi_onTwipEvent(twip_status);
606+
twip_mode = TWIPM_WAIT;
607+
twip_state = TWIP_WAIT_STOP;
635608
} else {
636-
twip_state = TWIP_REC_ACK;
609+
twip_status = TW_SR_DATA_ACK;
610+
twi_onTwipEvent(twip_status);
611+
bitCount = 8;
612+
twip_state = TWIP_READ;
637613
}
638614
}
639-
break;
640-
641-
case TWIP_REC_ACK:
642-
if (scl) {
643-
// ignore
644-
} else {
645-
SDA_HIGH();
646-
twip_state = TWIP_READ_ACK;
647-
}
648-
break;
615+
}
616+
} else IFSTATE(S2M(TWIP_SLA_R)|S2M(TWIP_WRITE)) {
617+
if (scl) {
618+
// ignore
619+
} else {
620+
bitCount--;
621+
(twi_data & 0x80) ? SDA_HIGH() : SDA_LOW();
622+
twi_data <<= 1;
649623

650-
case TWIP_READ_ACK:
651-
if (!scl) {
652-
// ignore
624+
if (bitCount != 0) {
625+
// continue
653626
} else {
654-
twi_ack_rec = !sda;
655-
twip_state = TWIP_RWAIT_ACK;
627+
twip_state = TWIP_REC_ACK;
656628
}
657-
break;
658-
659-
case TWIP_RWAIT_ACK:
660-
if (scl) {
661-
// ignore
629+
}
630+
} else IFSTATE(S2M(TWIP_REC_ACK)) {
631+
if (scl) {
632+
// ignore
633+
} else {
634+
SDA_HIGH();
635+
twip_state = TWIP_READ_ACK;
636+
}
637+
} else IFSTATE(S2M(TWIP_READ_ACK)) {
638+
if (!scl) {
639+
// ignore
640+
} else {
641+
twi_ack_rec = !sda;
642+
twip_state = TWIP_RWAIT_ACK;
643+
}
644+
} else IFSTATE(S2M(TWIP_RWAIT_ACK)) {
645+
if (scl) {
646+
// ignore
647+
} else {
648+
SCL_LOW(); // clock stretching
649+
if (twi_ack && twi_ack_rec) {
650+
twip_status = TW_ST_DATA_ACK;
651+
twi_onTwipEvent(twip_status);
652+
twip_state = TWIP_WRITE;
662653
} else {
663-
SCL_LOW(); // clock stretching
664-
if (twi_ack && twi_ack_rec) {
665-
twip_status = TW_ST_DATA_ACK;
666-
twi_onTwipEvent(twip_status);
667-
twip_state = TWIP_WRITE;
668-
} else {
669-
// we have no more data to send and/or the master doesn't want anymore
670-
twip_status = twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK;
671-
twi_onTwipEvent(twip_status);
672-
twip_mode = TWIPM_WAIT;
673-
twip_state = TWIP_WAIT_STOP;
674-
}
654+
// we have no more data to send and/or the master doesn't want anymore
655+
twip_status = twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK;
656+
twi_onTwipEvent(twip_status);
657+
twip_mode = TWIPM_WAIT;
658+
twip_state = TWIP_WAIT_STOP;
675659
}
676-
break;
677-
678-
default:
679-
break;
660+
}
680661
}
681662
}
682663

@@ -687,9 +668,9 @@ void ICACHE_RAM_ATTR onSdaChange(void)
687668
sda = SDA_READ();
688669
scl = SCL_READ();
689670

690-
if (scl) /* !DATA */ switch (twip_state)
691-
{
692-
case TWIP_IDLE:
671+
int twip_state_mask = S2M(twip_state);
672+
if (scl) { /* !DATA */
673+
IFSTATE(S2M(TWIP_IDLE)) {
693674
if (sda) {
694675
// STOP - ignore
695676
} else {
@@ -698,27 +679,14 @@ void ICACHE_RAM_ATTR onSdaChange(void)
698679
twip_state = TWIP_START;
699680
ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms
700681
}
701-
break;
702-
703-
case TWIP_START:
704-
case TWIP_REP_START:
705-
case TWIP_SEND_ACK:
706-
case TWIP_WAIT_ACK:
707-
case TWIP_SLA_R:
708-
case TWIP_REC_ACK:
709-
case TWIP_READ_ACK:
710-
case TWIP_RWAIT_ACK:
711-
case TWIP_WRITE:
682+
} else IFSTATE(S2M(TWIP_START)|S2M(TWIP_REP_START)|S2M(TWIP_SEND_ACK)|S2M(TWIP_WAIT_ACK)|S2M(TWIP_SLA_R)|S2M(TWIP_REC_ACK)|S2M(TWIP_READ_ACK)|S2M(TWIP_RWAIT_ACK)|S2M(TWIP_WRITE)) {
712683
// START or STOP
713684
SDA_HIGH(); // Should not be necessary
714685
twip_status = TW_BUS_ERROR;
715686
twi_onTwipEvent(twip_status);
716687
twip_mode = TWIPM_WAIT;
717688
twip_state = TWIP_BUS_ERR;
718-
break;
719-
720-
case TWIP_WAIT_STOP:
721-
case TWIP_BUS_ERR:
689+
} else IFSTATE(S2M(TWIP_WAIT_STOP)|S2M(TWIP_BUS_ERR)) {
722690
if (sda) {
723691
// STOP
724692
SCL_LOW(); // clock stretching
@@ -736,10 +704,7 @@ void ICACHE_RAM_ATTR onSdaChange(void)
736704
ets_timer_arm_new(&timer, twi_timeout_ms, false, true); // Once, ms
737705
}
738706
}
739-
break;
740-
741-
case TWIP_SLA_W:
742-
case TWIP_READ:
707+
} else IFSTATE(S2M(TWIP_SLA_W)|S2M(TWIP_READ)) {
743708
// START or STOP
744709
if (bitCount != 7) {
745710
// inside byte transfer - error
@@ -765,10 +730,7 @@ void ICACHE_RAM_ATTR onSdaChange(void)
765730
twip_mode = TWIPM_IDLE;
766731
}
767732
}
768-
break;
769-
770-
default:
771-
break;
733+
}
772734
}
773735
}
774736

0 commit comments

Comments
 (0)