#include #define PIN_OUT 0x60000300 #define PIN_OUT_SET 0x60000304 #define PIN_OUT_CLEAR 0x60000308 #define PIN_DIR 0x6000030C #define PIN_DIR_OUTPUT 0x60000310 #define PIN_DIR_INPUT 0x60000314 #define PIN_IN 0x60000318 #define _BV(x) ((1)<<(x)) #define DEBUG_HIGH s32i.n a14, a12, 0 #define DEBUG_LOW s32i.n a14, a13, 0 #define GPIO_STATUS_W1TC 0x60000324 #define GPIO_STATUS 0x6000031C //Detailed analysis of some useful stuff and performance tweaking: http://naberius.de/2015/05/14/esp8266-gpio-output-performance/ //Reverse engineerd boot room can be helpful, too: http://cholla.mmto.org/esp8266/bootrom/boot.txt //USB Protocol read from wikipedia: https://en.wikipedia.org/wiki/USB #define SIZE_OF_BUFFER 24 .global usb_buffer #define usb_buffer_end (usb_buffer + SIZE_OF_BUFFER) .global usb_buff_data .global gpio_intr gpio_intr: addi a1, a1, -60 //Must be 4 higher than highest pusher s32i.n a0, a1, 56 s32i.n a2, a1, 52 s32i.n a3, a1, 48 s32i.n a9, a1, 24 s32i.n a10, a1, 20 s32i.n a11, a1, 16 s32i.n a12, a1, 12 s32i.n a12, a1, 8 s32i.n a13, a1, 4 s32i.n a14, a1, 0 movi a11, PIN_IN movi a12, PIN_OUT_SET movi a13, PIN_OUT_CLEAR movi a14, 32 movi a2, 16 //GPIO Interrupt movi a0, _xtos_ints_off callx0 a0 DEBUG_HIGH //Now, we're in the interrupt handler, we can operate normally. //First, we need to find an edge. Wait for a high, then wait for it to go low. movi a9, 1023 #timeout find_high: l32i.n a2, a11, 0 bbsi a2, DPLUS, done_high addi.n a9, a9, -1 bnez a9, find_high j end_gpio_intr done_high: movi a9, 1023 #timeout find_low: l32i.n a2, a11, 0 bbci a2, DPLUS, done_low addi.n a9, a9, -1 bnez a9, find_low j end_gpio_intr done_low: //Careful here. We have only a few cycles to get the current time and enter the loop to find end of preamble. //You may notice that we "should" be reading the bit in the past. That is OKAY. //XXX TODO: Tune this offset to the optimal value. rsr a10, ccount addi a10, a10, -63 //Marks when we should read our bit. //Need to find the end of the preamble. movi a9, 1023 // timeout find_double_low: addi.n a9, a9, -1 beqz a9, end_gpio_intr addi a10, a10, 53 wait_double_loop_int: rsr a3, ccount bge a10, a3, wait_double_loop_int // Time to read a bit l32i.n a3, a11, 0 bbsi a3, DPLUS, find_double_low // Got a 0! Let's see if that happens again. addi a10, a10, 53 wait_double_loop_int2: rsr a3, ccount bge a10, a3, wait_double_loop_int2 # Poll again. l32i.n a3, a11, 0 bbsi a3, DPLUS, find_double_low //We have passed the preamble and are currently in the body of the USB request. //We actually have a little bit of time here to do things since we /just/ popped out. //Acquire a few more scratch registers. s32i.n a4, a1, 44 s32i.n a5, a1, 40 s32i.n a6, a1, 36 s32i.n a7, a1, 32 s32i.n a8, a1, 28 movi a8, usb_buffer movi a5, 0 //State buffer //State buffer: //Bit 0: Last State //Bit 1: \ //Bit 2: / Count for divisor // //Bit 4: \ //Bit 5: | One's Count //Bit 6: / // //Bit 8: # of bits present in output byte //Bit 9: //Bit 10: // //Bit 16-23: # output byte // //At this point we have the following registers: // a2: scratch** Truly // a3: scratch / input signal // a4: scratch** Truly // a5: State // a6: scratch (Free for CRC?) // a7: CRC??? // a8: Location of Output Buffer // a9: timeout? (sort of?) //a10: Tick timer //a11: Input Pin Address //a12: Out_set \ //a13: Out_clr | Debug bits //a14: debug bit / movi a9, 16 keep_going: addi.n a9, a9, -1 addi a10, a10, 60 //This actually needs to be 53.3333333, so every third frame, add an extra little bit. // extui a0, a5, 1, 2 // addi a0, a0, 1 // movi a3, 0xfffffff9 // and a5, a5, a3 // slli a0, a0, 1 // blti a0, 6, inner_wait //If the sign extended "6" is there (the third time through) don't skip. // addi a10, a10, 1 //Add an extra one (54) every third! // movi a0, 0 inner_wait: DEBUG_LOW // or a5, a5, a0 rsr a3, ccount bge a10, a3, inner_wait //Read our sample l32i.n a3, a11, 0 DEBUG_HIGH mov a4, a5 extui a0, a5, 4, 3 //Extract "Bit count" //Clear out bit count in actual status regiser. movi a2, 0xffffff8e and a5, a5, a2 //Update bit in state to match extui a2, a3, DPLUS, 1 //Update the last_state in a5. or a5, a5, a2 bbsi a3, DPLUS, dp_1 dp_0: bbci a3, DMINUS, handle_end_of_packet //Both D+ and D- are 0. (End of packet) bbci a4, 0, bit_same j bit_different dp_1: bbsi a3, DMINUS, handle_error //Both D+ and D- are 1 (Error) //D+ = 1, D- = 0 bbsi a4, 0, bit_same //A2 contains whether it was a 1 or a 0, A3, A4 are now free. A0 contains bit count. bit_different: //Different - logical '0' bgeui a0, 6, finish_bit //Implicitly, the # of 1's field in a5 is now zeroed. j continue_bit bit_same: //Was the bit the same as last time? This is a logical '1' //Detect if bit-stuffing error (Too many 1's) bgeui a0, 6, handle_error addi.n a0, a0, 1 //Increment one's count slli a0, a0, 4 or a5, a5, a0 //Write new one's into field. //A2 contains value of this bit. If it was bit stuffed, this code would not be called. //A3, A4, A6, A7 are free. A5 still contains system state. continue_bit: //Put the new bit into the status word. extui a0, a5, 16, 8 //Extract current word slli a3, a2, 23 //Shift bit up 23 bits. sra a0, a0 //Shift current word some movi a2, 0x00ffffff //Can't use extui since it's 24 bits. and a5, a5, a2 //Clear out the word to accept it back. or a5, a5, a0 //Write the byte back into a5. // extui a0, a5, 8, 3 //Extract the location of this new bit. addi a0, a0, 1 //Increment number of bits in this byte. movi a2, 0xfffff8ff //Clear out the # of bits in this word and a5, a5, a2 slli a3, a0, 8 or a5, a5, a3 //Fill out the bit field in our status word. bltui a0, 8, dont_have_byte //We have a byte, write it into the buffer. extui a0, a5, 16, 8 s8i a8, a0, 0 movi a0, 0xff00ffff and a5, a5, a0 addi a8, a8, 1 movi a0, usb_buffer_end bgeu a0, a7, handle_error //Detect overflows. //We get here if we had the bit or not, we still need to compute the CRC. dont_have_byte: movi a0, 0x14 //XXX TODO: Work on CRC. finish_bit: //We get here after a successful bit, or, we have a stuffed bit. bnez a9, keep_going handle_end_of_packet: movi a0, usb_buffer sub a8, a8, a0 movi a0, usb_buff_data s32i a0, a2, 0 j end_intr_with_extra_vars handle_error: DEBUG_LOW //TODO: Make a mode that waits for data to finish out, despite there being an error. movi a0, usb_buff_data movi a2, 0xffffffff s32i a0, a2, 0 end_intr_with_extra_vars: l32i.n a4, a1, 44 l32i.n a5, a1, 40 l32i.n a6, a1, 36 l32i.n a7, a1, 32 l32i.n a8, a1, 28 end_gpio_intr: DEBUG_LOW //Acknowledge interrupt movi a2, GPIO_STATUS l32i.n a3, a2, 0 movi a2, GPIO_STATUS_W1TC s32i.n a3, a2, 0 movi.n a2, 16 movi a0, _xtos_ints_on callx0 a0 l32i.n a0, a1, 56 l32i.n a2, a1, 52 l32i.n a3, a1, 48 l32i.n a9, a1, 24 l32i.n a10, a1, 20 l32i.n a11, a1, 16 l32i.n a12, a1, 12 l32i.n a12, a1, 8 l32i.n a13, a1, 4 l32i.n a14, a1, 0 addi a1, a1, 60 ret.n ill