' Copyright Brad Campbell, 2007 - All rights reserved ' ' 3 IO pins used. Enable, D+, D- ' Current configuration. ' 0 - D- ' 1 - D+ ' 2 - Enable ' Tie 2 to 0 with a 3.3k resistor and put a pair of 68Ohm resistors in series with D+ and D- to the host ' While the specification dictates we must turn around in 6.5 or 7.5 bit times, it also says a host must ' wait at least 16 and not more than 18 bit times for a response. ' 5 hubs + cable propagation delay is 760ns which equates to 1.14 bit times, leaving us 14.8 bit times ' to effectively turn around in. This is about 196 instructions. ' Endpoint 0 has a common TX/RX toggle bit. All other endpoints have separate TX/RX toggle bits PUB Start(crctable, buffers) : OK | i Pcrcmask := crctable epb := buffers K := 1 << 1 J := 1 << 0 Enable := 1 << 2 OK := cognew(@USB_STACK, 0) DAT USB_STACK org mov packetcnt, #0 mov packetfail, #0 mov address, #0 mov bmask, K or bmask, J mov paddr, #0 call #crc5_address wrlong allones, epb ' Semaphore to wait for app thread to be running :sloop rdlong da, epb test da, allones wz IF_NZ jmp #:sloop ' Loop until it is or outa, enable or dira, enable ' Tell the host we are alive! jmp #wfs ' -------------------------------------------- ' Receive low level routines ' -------------------------------------------- rxreload add bufptr, #1 ' 12 post inc pointer to allow next mov to pipeline rxindr mov 0-0, da ' 13 store contents of buffer mov bitcnt, #32 ' 14 reload bit counter rx test K, ina wz ' 1 - 13 bits muxz db, emask wc ' 2 - top 2 bits contain previous and new values.. shl db, #1 ' 3 - parity check 00 or 11 is odd and 01 or 10 is even test bmask, ina wz ' 4 - Check for EOP IF_Z jmp #wfe ' 5 rcr da, #1 ' 6 - rotate carry from xor into top bit of acc rcl dc, #1 ' 7 - Bottom 7 bits are all 0 we need to unstuff and dc, #$3F wz ' 8 - test is inverted as input bits are inverted IF_Z call #rxunstuff ' 9 - we need to unstuff next bit sub bitcnt, #1 wz ' 10 IF_Z jmp #rxreload ' 11 movd rxindr, bufptr ' 12 - This saves a cycle in #l1 and does not hurt to keep repeating it nop test K, ina wz ' 1 - 13 bits muxz db, emask wc ' 2 - top 2 bits contain previous and new values.. shl db, #1 ' 3 - parity check 00 or 11 is odd and 01 or 10 is even test bmask, ina wz ' 4 - Check for EOP IF_Z jmp #wfe ' 5 rcr da, #1 ' 6 - rotate carry from xor into top bit of acc rcl dc, #1 ' 7 - Bottom 7 bits are all 0 we need to unstuff and dc, #$3F wz ' 8 - test is inverted as input bits are inverted IF_Z call #rxunstuff ' 9 - we need to unstuff next bit sub bitcnt, #1 wz ' 10 IF_Z jmp #rxreload ' 11 movd rxindr, bufptr ' 12 nop test K, ina wz ' 1 - 14 bits muxz db, emask wc ' 2 - top 2 bits contain previous and new values.. shl db, #1 ' 3 - parity check 00 or 11 is odd and 01 or 10 is even test bmask, ina wz ' 4 - Check for EOP IF_Z jmp #wfe ' 5 rcr da, #1 ' 6 - rotate carry from xor into top bit of acc rcl dc, #1 ' 7 - Bottom 7 bits are all 0 we need to unstuff and dc, #$3F wz ' 8 - test is inverted as input bits are inverted IF_Z call #rxunstuff ' 9 - we need to unstuff next bit sub bitcnt, #1 wz ' 10 IF_Z jmp #rxreload ' 11 movd rxindr, bufptr ' 12 nop ' 13 jmp #rx ' 14 rxunstuff nop ' 10 nop ' 11 nop ' 12 nop ' 13 test K, ina wz ' 1 muxz db, emask wc ' 2 shl db, #1 ' 3 rcl dc, #1 ' 4 test bmask, ina wz ' 5 IF_Z jmp #wfe ' 6 test dc, #$7F wz ' 7 IF_Z or flags, flag_serr ' 8 - Stuffing error! 7 1's in a row!! rxunstuff_ret ret ' 9 wfs mov bitcnt, #32 ' Wait For Start mov dc, #$FF ' - Stuff register is inverted mov bufptr, #buf1 ' Init indirect buffer pointer - need to check this mov db, #0 ' Zero xor register mov flags, zero ' Set up status flags wfhere :wfs0 mov da, ina and da, bmask wz IF_Z jmp #wait_reset cmp da, K wz IF_NZ jmp #:wfs0 ' K :wfs1 waitpeq J, bmask :wfs2 waitpeq K, bmask mov dd, cnt add dd, #13 ' Delay half bit time - calcs waitcnt dd, #53 mov da, ina and da, bmask cmp da, K wz IF_NZ jmp #:wfs1 ' K waitcnt dd, #53 mov da, ina and da, bmask cmp da, K wz IF_NZ jmp #:wfs1 ' K waitcnt dd, #53 jmp #rx wait_reset add reset, #1 test bmask, ina wz IF_Z jmp #wait_reset cmp reset, #$1FF wc mov reset, #0 IF_C jmp #wfs ' Not long enough to be a reset mov address, #0 mov eps, #$FF mov state, #0 mov paddr, #0 call #crc5_address wrlong flag_reset, epb waitpeq J, bmask jmp #wfhere wfe ' Wait For End movd :wind, bufptr add bitcnt, #3 ' Round bitcount up to compensate for ' Dodgy timing and bitcnt, #$F8 ' Scrape off lower 3 bits ' Shift final word to sit properly in lsb order shr da, bitcnt :wind mov 0-0, da cmp bitcnt, #18 wc ' Not even one full byte in IF_BE jmp #process_inbound ' 7 to here jmp #wfs ' -------------------------------------------- ' Transmit low level routines ' Transmit speed is fast by 0.625% (spec says we can be up to 1.5% so it's all good) ' Most of the transmit loop is instruction critical and it *will* lock up if you get it wrong ' If you are lucky it will lock up on a J, but if it locks up on a K or SOP it *will* take out the ' USB port and require a restart. Testing through a HUB is a good idea as you can cycle the HUB. ' -------------------------------------------- ' Bitstuff the next bit txs waitcnt dd, #53 xor outa, bmask shl db, #1 or db, #1 txs_ret ret ' Move onto the next byte but invert it first (we transmit inverted to make stuffing easy) txreload mov bitcnt, #8 txreload_ind mov da, 0-0 sub bytecnt, #1 wz IF_Z jmp #tx_eop ' Put this here rather than the end to give us xor da, #$FF ' A spare cycle or two ensuring the EOP waitcnt add txreload_ind, #1 ' does not hang tx1 waitcnt dd, #53 stx1 rcr da, #1 wc IF_C xor outa, bmask rcl db, #1 and db, #$3F wz IF_Z call #txs tx1e sub bitcnt, #1 wz test da, #1 wz ' Placing this here gives us a little extra shl db, #1 ' room in the reload routine to allow xor of muxnz db, #1 ' data and proper indirect loading and db, #$3F wz ' Transmitting pairs of bits ping/pong buys us this sub bitcnt, #1 tx2 waitcnt dd, #53 rcr da, #1 wc IF_C xor outa, bmask tjnz db, #tx2r call #txs tx2r tjz bitcnt, #txreload jmp #tx1 tx_eop waitcnt dd, #53 ' This delay is *tight* andn outa, bmask ' Start the EOP mov dd, cnt add dd, #106 ' Delay 2 bit times waitcnt dd, #53 or outa, J ' Force a J waitcnt dd, #53 ' Delay one bit time... and andn dira, bmask ' Switch off the drivers andn outa, bmask jmp #wfs do_tx mov bitcnt, #8 mov da, #$7F ' Sync byte (inverted as we never xor that one) movs txreload_ind, #b_tkn ' Set up indirect reload register add bytecnt, #1 ' Add sync byte to count mov db, #$FF ' Stuff register is inverted andn outa, bmask or outa, J ' Set pins up as a J before.... or dira, bmask ' enabling the drivers mov dd, cnt add dd, #57 ' Proper time for us to get settled jmp #stx1 ' .... and away we go ' -------------------------------------------- ' Protocol stack routines ' -------------------------------------------- ' Responses to setup queries in the STATUS phase must go out as DATA1 send_empty_data1 mov b_1, #0 mov b_2, #0 mov b_tkn, #$4B mov bytecnt, #3 jmp #do_tx send_ack mov b_tkn, #$D2 ' PID ACK mov bytecnt, #1 jmp #do_tx send_nak mov b_tkn, #$5A ' PID NAK mov bytecnt, #1 jmp #do_tx pid_setup or state, flag_setup or eps, #$11 ' Set up toggle bits jmp #wfs pid_out andn state, flag_setup ' or state, flag_out jmp #wfs pid_in mov da, epoint shl da, #4 ' Multiply by 8 add da, epb ' Add eps offset add da, #64 ' Output endpoints rdlong db, da wz ' Endpoint out byte counter IF_Z jmp #send_nak ' Nothing to send mov dc, db andn dc, #$FF ' Clear the bottom 8 bits (byte counter) or state, dc ' Set any flags passed down from the application stack ' test db, flag_rtoggle wz ' IF_NZ or eps, #$22 test db, flag_empty1 wz ' Are we in the STATUS phase of a setup query? IF_NZ jmp #send_empty_data1 ' ... Send an empty DATA1 packet test db, flag_empty wz ' Send an empty packet with the next logical DATA0/1 sequence IF_NZ jmp #:pid_send_empty ' Token (BULK/INT out where last data was packet aligned) mov bytecnt, db and bytecnt, #$0F ' Clamp max in case app level goes haywire add da, #4 ' Unpack the bytes from here down (inline for speed) rdlong db, da ' Endpoint out bytes [0-3] mov b_1, db shr db, #8 mov b_2, db shr db, #8 mov b_3, db shr db, #8 mov b_4, db add da, #4 rdlong db, da ' Endpoint out bytes [4-7] mov b_5, db shr db, #8 mov b_6, db shr db, #8 mov b_7, db shr db, #8 mov b_8, db add da, #4 rdlong db, da ' Endpoint out bytes [8-9] mov b_9, db shr db, #8 mov b_10, db jmp #:pid_next :pid_send_empty mov b_1, #0 ' This is a cheat, but the CRC on an empty packet is $00 $00 mov b_2, #0 mov bytecnt, #2 :pid_next mov da, #1 shl da, epoint test epoint, allones wz IF_NZ shl da, #4 test eps, da wz ' Make sure data toggle is correct for token IF_NZ mov b_tkn, #$C3 IF_Z mov b_tkn, #$4B add bytecnt, #1 ' Allow for token jmp #do_tx pid_data test state, flag_setup wz IF_Z jmp #:data_1 ' Is this a setup packet?? or flags, flag_setup ' Tell upper layer this is a setup request test b_1, #$20 wz ' Class packet? IF_NZ or flags, flag_class ' Tell upper layer this is a class request mov da, b_2 ' Get bREQUEST and da, #$FF ' cmp da, #11 wz ' If this is a SET_CONFIGURATION request .... ' IF_Z mov eps, #$FF ' Reset toggle bits cmp da, #5 wz ' Is this a SET_ADDRESS request? IF_NZ jmp #:data_2 mov da, b_3 ' Get new address and da, #$7F ' Addresses are 7 bits mov paddr, da ' Make it pending until application layer has completed transaction ' jmp #:data_2 :data_1 ' test state, flag_out wz ' Not a setup packet, have we just had an PID_OUT? ' IF_Z jmp #wfs ' Packet out of sequence! :data_2 mov da, epoint shl da, #4 ' Multiply by 4 add da, epb ' Add endpoint buffer offset rdlong db, da wz ' Endpoint byte counter IF_NZ jmp #send_nak ' Other thread is not finished with this buffer ' Shuffle to convert buffers (we need only 1st 8 bytes as we don't want CRC anyway) ' IN X987 6543 210X (Byte X - next to byte 0 - is the token. Not required) ' OUT 7654 3210 mov db, buf1 shr db, #8 mov dc, buf1a shl dc, #24 or db, dc ' db is 1st long mov dd, buf1a shr dd, #8 mov dc, buf1b shl dc, #24 or dd, dc ' dd is 2nd long ' Write buffer for notice of application thread ' Always write byte count / flags last to ensure we don't try to read while ' the buffer is half written add da, #4 ' Endpoint bytes [0-3] wrlong db, da ' Write 1st long add da, #4 ' Endpoint bytes [4-7] sub bytecnt, #3 ' Minus token and CRC wrlong dd, da ' Write 2nd long sub da, #8 or bytecnt, flags wrlong bytecnt, da ' Number of bytes in andn state, flag_setup ' Clear the setup flag if it was set mov da, #1 shl da, epoint xor eps, da ' Toggle recieve toggle bit jmp #send_ack pid_data0 call #crc16check test flags, flag_crc wz IF_NZ jmp #wfs ' Packet was bad mov da, #1 shl da, epoint test eps, da wz IF_Z jmp #send_ack ' We already have this data packet jmp #pid_data pid_data1 call #crc16check test flags, flag_crc wz IF_NZ jmp #wfs ' Packet was bad mov da, #1 shl da, epoint test eps, da wz IF_NZ jmp #send_ack ' We already have this data packet jmp #pid_data pid_ack mov da, #1 shl da, epoint test epoint, allones wz IF_NZ shl da, #4 xor eps, da ' Toggle endpoint state mov da, epoint shl da, #4 ' Multiply by 8 add da, epb ' Add eps offset add da, #64 ' Output endpoints wrlong zero, da ' Tell other thread we are ready for more data test state, flag_address wz ' on this endpoint IF_Z jmp #wfs mov address, paddr ' Set the new address andn state, flag_address ' Clear the flag baby! call #crc5_address jmp #wfs ' This is about 3x the speed of doing it in a loop one byte at a time ' 23 instructions unpack_buffer mov da, buf1 mov b_tkn, da shr da, #8 mov b_1, da shr da, #8 mov b_2, da shr da, #8 mov b_3, da mov da, buf1a mov b_4, da shr da, #8 mov b_5, da shr da, #8 mov b_6, da shr da, #8 mov b_7, da mov da, buf1b mov b_8, da shr da, #8 mov b_9, da shr da, #8 mov b_10, da unpack_buffer_ret ret ' This simply writes the inbound usb data to the buffer in the hub ' Oh, and checks CRC's now too. They could use some optimisation! ' Mangles da, db, dc, dd, bufptr, t, t1, data process_inbound test flags, flag_serr wz IF_NZ jmp #wfs ' Bitstuffing error detected xor buf1, allones xor buf1a, allones xor buf1b, allones call #unpack_buffer ' This next bit is essential as the hub code uses it to tell how many longs to pull ' and display as this inbound message ' This calculates the number of bytes in the message mov bytecnt, bufptr ' 36 to here sub bytecnt, #buf1 shl bytecnt, #2 mov da, #$20 sub da, bitcnt shr da, #3 or bytecnt, da and b_tkn, #$FF ' data in and data out need address and endpoint of last token ' packet cmp b_tkn, #$c3 wz ' Data0 IF_Z jmp #pid_data0 cmp b_tkn, #$4b wz ' Data1 IF_Z jmp #pid_data1 ' andn state, flag_out cmp b_tkn, #$D2 wz ' Ack IF_Z jmp #pid_ack :next mov inaddr, b_1 and inaddr, #$7F cmp inaddr, address wz IF_NZ jmp #wfs ' Not addressed to us! call #crc5check ' 55 to here test flags, flag_crc wz ' Token failed CRC check IF_NZ jmp #wfs mov da, b_1 shr da, #7 mov epoint, da and epoint, #$F cmp b_tkn, #$E1 wz ' OUT IF_Z jmp #pid_out cmp b_tkn, #$69 wz ' IN IF_Z jmp #pid_in cmp b_tkn, #$2D wz ' Setup IF_Z jmp #pid_setup jmp #wfs ' Obviously not a valid token ' Calculate crc of packet using pre-calculated address crc and comparing the last 5 bits ' Now fast enough to be used in line. crc5check mov crc, crc5addr mov da, #4 mov db, b_2 shl db, #1 mov dc, b_1 shr dc, #7 or dc, db call #crc5bit mov da, crc shl crc, #3 xor crc, allones xor crc, b_2 and crc, #$F8 wz IF_NZ or flags, flag_crc crc5check_ret ret ' Precaculate the address into the CRC register when we change address to save 7 loops ' of the crc routine crc5_address mov crc, #$1F mov dc, paddr mov da, #7 call #crc5bit mov crc5addr, crc crc5_address_ret ret ' This is horridly slow. crc5bit ' dc has the byte to add to the crc | da has the number of bits to process and dc, #$FF crc5bitloop mov db, dc xor db, crc shr dc, #1 shr crc, #1 shr db, #1 wc if_c xor crc, #$14 djnz da, #crc5bitloop crc5bit_ret ret ' Perform a crc check on recieved data packets ' Mangles da, db, dc ' Returns crc crcmask long $0000FFFF crc16check ' 141 Instruction for a full buffer mov crc, crcmask movs :crc16indr, #b_1 mov da, bytecnt sub da, #3 test da, allones wz IF_Z jmp #crc16check_ret ' This dies horribly if we pass it a no-data packet :crc16indr mov dc, 0-0 add :crc16indr, #1 xor dc, crc and dc, #$FF shl dc, #1 add dc, Pcrcmask rdword db, dc shr crc, #8 xor crc, db djnz da, #:crc16indr ' This extra complexity saves us 2 loops (up to 30 instruction cycles and costs us 12 extra) ' Net gain of 18 instructions or 1.5 bit times xor crc, allones and crc, crcmask mov :crc16indr1, :crc16indr add :crc16indr, #1 :crc16indr1 nop ' Indirect move and dc, #$FF ' 2 mov :crc16indr2, :crc16indr mov da, dc :crc16indr2 nop ' Indirect move and dc, #$FF ' 2 shl dc, #8 or dc, da cmp crc, dc wz IF_NZ or flags, flag_crc crc16check_ret ret deadbeef long $DEADBEEF reset long 0 allones long $FFFFFFFF zero long 0 ' These 5 are loaded in spin prior to starting the cog Pcrcmask long 0 ' Pointer to CRC table in hub K long 0 ' D+ line J long 0 ' D- line enable long 0 ' Enable enumeration pullup epb long 0 bmask long 0 ' Bitmask of K & J together emask long $40000000 ' MUX mask for RX inbound xor register 'flag_out long 1 << 21 flag_reset long 1 << 22 'flag_rtoggle long 1 << 23 flag_empty1 long 1 << 24 ' Send an empty DATA1 packet 'flag_debug long 1 << 25 ' Debugging used in the app level flag_class long 1 << 26 ' We recieved a CLASS request flag_address long 1 << 27 ' Inbound setup packet is an address request flag_empty long 1 << 28 ' Send next logical DATAX token in an empty packet flag_setup long 1 << 29 ' Inbound packet is a setup request flag_serr long 1 << 30 ' Bit stuffing error detected flag_crc long 1 << 31 ' CRC error detected eps res 1 ' endpoint status token res 1 ' inbound token inaddr res 1 ' Inbound token device address address res 1 ' device address epoint res 1 ' inbound endpoint state res 1 ' State machine flags paddr res 1 ' Pending address to be set at end of STATUS cycle bitcnt res 1 ' Unpacked data buffer b_tkn res 1 b_1 res 1 b_2 res 1 b_3 res 1 b_4 res 1 b_5 res 1 b_6 res 1 b_7 res 1 b_8 res 1 b_9 res 1 b_10 res 1 ' Packed data buffer buf1 res 1 ' TX/RX Buffer buf1a res 1 buf1b res 1 ' Indirect buffer pointer bufptr res 1 ' Buffer pointer ' Misc temporary registers da res 1 db res 1 dc res 1 dd res 1 flags res 1 ' Recieve status flags ' TX / RX bytes in or bytes to send bytecnt res 1 ' Cumulative crc register crc res 1 packetcnt res 1 packetfail res 1 ' Pre-calculated crc load based on device address crc5addr res 1 fit $1F0