Synopsys OUT EP improvements:
- Use register based XFRSIZ to determine transfer complete (xfer->queued_len and xfer->short_packet were deleted) - Pop out as many RxFIFO data entries as available within a IRQ call - less application interruption due to XFRC calls
This commit is contained in:
		@@ -101,9 +101,7 @@ static uint8_t _setup_offs; // We store up to 3 setup packets.
 | 
				
			|||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  uint8_t * buffer;
 | 
					  uint8_t * buffer;
 | 
				
			||||||
  uint16_t total_len;
 | 
					  uint16_t total_len;
 | 
				
			||||||
  uint16_t queued_len;
 | 
					 | 
				
			||||||
  uint16_t max_size;
 | 
					  uint16_t max_size;
 | 
				
			||||||
  bool short_packet;
 | 
					 | 
				
			||||||
} xfer_ctl_t;
 | 
					} xfer_ctl_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef volatile uint32_t * usb_fifo_t;
 | 
					typedef volatile uint32_t * usb_fifo_t;
 | 
				
			||||||
@@ -356,8 +354,6 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
 | 
				
			|||||||
  xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
 | 
					  xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
 | 
				
			||||||
  xfer->buffer       = buffer;
 | 
					  xfer->buffer       = buffer;
 | 
				
			||||||
  xfer->total_len    = total_bytes;
 | 
					  xfer->total_len    = total_bytes;
 | 
				
			||||||
  xfer->queued_len   = 0;
 | 
					 | 
				
			||||||
  xfer->short_packet = false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint16_t num_packets = (total_bytes / xfer->max_size);
 | 
					  uint16_t num_packets = (total_bytes / xfer->max_size);
 | 
				
			||||||
  uint8_t short_packet_size = total_bytes % xfer->max_size;
 | 
					  uint8_t short_packet_size = total_bytes % xfer->max_size;
 | 
				
			||||||
@@ -379,9 +375,10 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
 | 
				
			|||||||
      dev->DIEPEMPMSK |= (1 << epnum);
 | 
					      dev->DIEPEMPMSK |= (1 << epnum);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    // Each complete packet for OUT xfers triggers XFRC.
 | 
					    // A full OUT transfer (multiple packets, possibly) triggers XFRC.
 | 
				
			||||||
    out_ep[epnum].DOEPTSIZ |= (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) |
 | 
					    out_ep[epnum].DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT_Msk | USB_OTG_DOEPTSIZ_XFRSIZ);
 | 
				
			||||||
                              ((xfer->max_size & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos);
 | 
					    out_ep[epnum].DOEPTSIZ |= (num_packets << USB_OTG_DOEPTSIZ_PKTCNT_Pos) |
 | 
				
			||||||
 | 
					                              ((total_bytes << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) & USB_OTG_DOEPTSIZ_XFRSIZ_Msk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
 | 
					    out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -477,65 +474,33 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/*------------------------------------------------------------------*/
 | 
					/*------------------------------------------------------------------*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Split into "receive on endpoint 0" and "receive generic"; endpoint 0's
 | 
					// Read a single data packet from receive FIFO
 | 
				
			||||||
// DOEPTSIZ register is smaller than the others, and so is insufficient for
 | 
					static void read_fifo_packet(uint8_t * dst, uint16_t len){
 | 
				
			||||||
// determining how much of an OUT transfer is actually remaining.
 | 
					 | 
				
			||||||
static void receive_packet(xfer_ctl_t * xfer, /* USB_OTG_OUTEndpointTypeDef * out_ep, */ uint16_t xfer_size) {
 | 
					 | 
				
			||||||
  usb_fifo_t rx_fifo = FIFO_BASE(0);
 | 
					  usb_fifo_t rx_fifo = FIFO_BASE(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // See above TODO
 | 
					  // Reading full available 32 bit words from fifo
 | 
				
			||||||
  // uint16_t remaining = (out_ep->DOEPTSIZ & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
 | 
					  uint16_t full_words = len >> 2;
 | 
				
			||||||
  // xfer->queued_len = xfer->total_len - remaining;
 | 
					  for(uint16_t i = 0; i < full_words; i++) {
 | 
				
			||||||
 | 
					    uint32_t tmp = *rx_fifo;
 | 
				
			||||||
  uint16_t remaining = xfer->total_len - xfer->queued_len;
 | 
					    dst[0] = tmp & 0x000000FF;
 | 
				
			||||||
  uint16_t to_recv_size;
 | 
					    dst[1] = (tmp & 0x0000FF00) >> 8;
 | 
				
			||||||
 | 
					    dst[2] = (tmp & 0x00FF0000) >> 16;
 | 
				
			||||||
  if(remaining <= xfer->max_size) {
 | 
					    dst[3] = (tmp & 0xFF000000) >> 24;
 | 
				
			||||||
    // Avoid buffer overflow.
 | 
					    dst += 4;
 | 
				
			||||||
    to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    // Room for full packet, choose recv_size based on what the microcontroller
 | 
					 | 
				
			||||||
    // claims.
 | 
					 | 
				
			||||||
    to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint8_t to_recv_rem = to_recv_size % 4;
 | 
					  // Read the remaining 1-3 bytes from fifo
 | 
				
			||||||
  uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
 | 
					  uint8_t bytes_rem = len & 0x03;
 | 
				
			||||||
 | 
					  if(bytes_rem != 0) {
 | 
				
			||||||
  // Do not assume xfer buffer is aligned.
 | 
					    uint32_t tmp = *rx_fifo;
 | 
				
			||||||
  uint8_t * base = (xfer->buffer + xfer->queued_len);
 | 
					    dst[0] = tmp & 0x000000FF;
 | 
				
			||||||
 | 
					    if(bytes_rem > 1) {
 | 
				
			||||||
  // This for loop always runs at least once- skip if less than 4 bytes
 | 
					      dst[1] = (tmp & 0x0000FF00) >> 8;
 | 
				
			||||||
  // to collect.
 | 
					    }
 | 
				
			||||||
  if(to_recv_size >= 4) {
 | 
					    if(bytes_rem > 2) {
 | 
				
			||||||
    for(uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
 | 
					      dst[2] = (tmp & 0x00FF0000) >> 16;
 | 
				
			||||||
      uint32_t tmp = (* rx_fifo);
 | 
					 | 
				
			||||||
      base[i] = tmp & 0x000000FF;
 | 
					 | 
				
			||||||
      base[i + 1] = (tmp & 0x0000FF00) >> 8;
 | 
					 | 
				
			||||||
      base[i + 2] = (tmp & 0x00FF0000) >> 16;
 | 
					 | 
				
			||||||
      base[i + 3] = (tmp & 0xFF000000) >> 24;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Do not read invalid bytes from RX FIFO.
 | 
					 | 
				
			||||||
  if(to_recv_rem != 0) {
 | 
					 | 
				
			||||||
    uint32_t tmp = (* rx_fifo);
 | 
					 | 
				
			||||||
    uint8_t * last_32b_bound = base + to_recv_size_aligned;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    last_32b_bound[0] = tmp & 0x000000FF;
 | 
					 | 
				
			||||||
    if(to_recv_rem > 1) {
 | 
					 | 
				
			||||||
      last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if(to_recv_rem > 2) {
 | 
					 | 
				
			||||||
      last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  xfer->queued_len += xfer_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Per USB spec, a short OUT packet (including length 0) is always
 | 
					 | 
				
			||||||
  // indicative of the end of a transfer (at least for ctl, bulk, int).
 | 
					 | 
				
			||||||
  xfer->short_packet = (xfer_size < xfer->max_size);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Write a single data packet to EPIN FIFO
 | 
					// Write a single data packet to EPIN FIFO
 | 
				
			||||||
@@ -564,11 +529,10 @@ static void write_fifo_packet(uint8_t fifo_num, uint8_t * src, uint16_t len){
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) {
 | 
					static void handle_rxflvl_ints(USB_OTG_OUTEndpointTypeDef * out_ep) {
 | 
				
			||||||
  usb_fifo_t rx_fifo = FIFO_BASE(0);
 | 
					  usb_fifo_t rx_fifo = FIFO_BASE(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Pop control word off FIFO (completed xfers will have 2 control words,
 | 
					  // Pop control word off FIFO
 | 
				
			||||||
  // we only pop one ctl word each interrupt).
 | 
					 | 
				
			||||||
  uint32_t ctl_word = USB_OTG_FS->GRXSTSP;
 | 
					  uint32_t ctl_word = USB_OTG_FS->GRXSTSP;
 | 
				
			||||||
  uint8_t pktsts = (ctl_word & USB_OTG_GRXSTSP_PKTSTS_Msk) >> USB_OTG_GRXSTSP_PKTSTS_Pos;
 | 
					  uint8_t pktsts = (ctl_word & USB_OTG_GRXSTSP_PKTSTS_Msk) >> USB_OTG_GRXSTSP_PKTSTS_Pos;
 | 
				
			||||||
  uint8_t epnum = (ctl_word &  USB_OTG_GRXSTSP_EPNUM_Msk) >>  USB_OTG_GRXSTSP_EPNUM_Pos;
 | 
					  uint8_t epnum = (ctl_word &  USB_OTG_GRXSTSP_EPNUM_Msk) >>  USB_OTG_GRXSTSP_EPNUM_Pos;
 | 
				
			||||||
@@ -580,7 +544,13 @@ static void read_rx_fifo(USB_OTG_OUTEndpointTypeDef * out_ep) {
 | 
				
			|||||||
    case 0x02: // Out packet recvd
 | 
					    case 0x02: // Out packet recvd
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
 | 
					        xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
 | 
				
			||||||
        receive_packet(xfer, bcnt);
 | 
					
 | 
				
			||||||
 | 
					        // Use BCNT to calculate correct bytes before data entry popped out from RxFIFO
 | 
				
			||||||
 | 
					        uint16_t remaining_bytes = ((out_ep[epnum].DOEPTSIZ & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) \
 | 
				
			||||||
 | 
					                                    >> USB_OTG_DOEPTSIZ_XFRSIZ_Pos) + bcnt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Read packet off RxFIFO
 | 
				
			||||||
 | 
					        read_fifo_packet((xfer->buffer + xfer->total_len - remaining_bytes), bcnt);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    case 0x03: // Out packet done (Interrupt)
 | 
					    case 0x03: // Out packet done (Interrupt)
 | 
				
			||||||
@@ -619,25 +589,10 @@ static void handle_epout_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTy
 | 
				
			|||||||
        _setup_offs = 0;
 | 
					        _setup_offs = 0;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // OUT XFER complete (single packet).
 | 
					      // OUT XFER complete
 | 
				
			||||||
      if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_XFRC) {
 | 
					      if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_XFRC) {
 | 
				
			||||||
        out_ep[n].DOEPINT = USB_OTG_DOEPINT_XFRC;
 | 
					        out_ep[n].DOEPINT = USB_OTG_DOEPINT_XFRC;
 | 
				
			||||||
 | 
					        dcd_event_xfer_complete(0, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
 | 
				
			||||||
        // TODO: Because of endpoint 0's constrained size, we handle XFRC
 | 
					 | 
				
			||||||
        // on a packet-basis. The core can internally handle multiple OUT
 | 
					 | 
				
			||||||
        // packets; it would be more efficient to only trigger XFRC on a
 | 
					 | 
				
			||||||
        // completed transfer for non-0 endpoints.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Transfer complete if short packet or total len is transferred
 | 
					 | 
				
			||||||
        if(xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
 | 
					 | 
				
			||||||
          xfer->short_packet = false;
 | 
					 | 
				
			||||||
          dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          // Schedule another packet to be received.
 | 
					 | 
				
			||||||
          out_ep[n].DOEPTSIZ |= (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) | \
 | 
					 | 
				
			||||||
              ((xfer->max_size & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) << USB_OTG_DOEPTSIZ_XFRSIZ_Pos);
 | 
					 | 
				
			||||||
          out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -682,11 +637,8 @@ static void handle_epin_ints(USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointType
 | 
				
			|||||||
            break;
 | 
					            break;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // TODO: queued_len can be removed later
 | 
					 | 
				
			||||||
          xfer->queued_len = xfer->total_len - remaining_bytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // Push packet to Tx-FIFO
 | 
					          // Push packet to Tx-FIFO
 | 
				
			||||||
          write_fifo_packet(n, (xfer->buffer + xfer->queued_len), packet_size);
 | 
					          write_fifo_packet(n, (xfer->buffer + xfer->total_len - remaining_bytes), packet_size);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Turn off TXFE if all bytes are written.
 | 
					        // Turn off TXFE if all bytes are written.
 | 
				
			||||||
@@ -756,12 +708,14 @@ void dcd_int_handler(uint8_t rhport) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if(int_status & USB_OTG_GINTSTS_RXFLVL) {
 | 
					  // Use while loop to handle more than one fifo data entry
 | 
				
			||||||
 | 
					  // within a single interrupt call
 | 
				
			||||||
 | 
					  while(USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_RXFLVL) {
 | 
				
			||||||
    // RXFLVL bit is read-only
 | 
					    // RXFLVL bit is read-only
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Mask out RXFLVL while reading data from FIFO
 | 
					    // Mask out RXFLVL while reading data from FIFO
 | 
				
			||||||
    USB_OTG_FS->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
 | 
					    USB_OTG_FS->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
 | 
				
			||||||
    read_rx_fifo(out_ep);
 | 
					    handle_rxflvl_ints(out_ep);
 | 
				
			||||||
    USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
 | 
					    USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user