Search This Blog

Apr 24, 2013

FATFs DMA support



We have request to add DMA support in FATFs to improve SD read/write performance.
1.       Add DMA descriptor structures in circurlar_buffer.c
        +SI32_DMADESC_A_Type dma_desc[SI32_DMACTRL_NUM_CHANNELS + SI32_DMADESC_ALT_STRIDE]  __attribute__ ((aligned SI32_DMADESC_PRI_ALIGN));
15        +
16        +
17        +void DMA_initialize(void)
18        +{
19        +    // Setup and enable the DMA controller and DMAXBAR
20        +    SI32_DMACTRL_A_write_baseptr(SI32_DMACTRL_0, (uint32_t)dma_desc);
21        +    SI32_DMACTRL_A_enable_module(SI32_DMACTRL_0);
22        +}
23        +
2.       Enable DMA clock and module in gCLKCTRL.c.
old        new        
...        ...        @@ -44,5 +44,7 @@ void CLKCTRL_setup_default_mode_clock_gates(void)
44        44                                                  SI32_CLKCTRL_A_APBCLKG0_USB0 |
45        45                                                  SI32_CLKCTRL_A_APBCLKG0_TIMER0 |
46        46                                                  SI32_CLKCTRL_A_APBCLKG0_TIMER1 |
47        +                                         SI32_CLKCTRL_A_APBCLKG1_MISC1 |
47        48                                                  SI32_CLKCTRL_A_APBCLKG0_FLASHCTRL0);
49        +  SI32_CLKCTRL_A_enable_ahb_to_dma_controller(SI32_CLKCTRL_0);
48        50         }
3.       Enable SPI DMA configuration in gModes.c.
old        new        
...        ...        @@ -38,8 +38,8 @@ void enter_default_mode_from_reset(void)
38        38        
39        39           // Setup ports
40        40           pb_enter_default_mode_from_reset();
41                -  gSPI0_enter_master_mode_config();
42                -
41        +  //gSPI0_enter_master_mode_config();
42        +  gSPI0_enter_dma_master_mode_config();
43        43           // Initialize clock control
44        44           SystemCoreClock = 20000000;
45        45           cpu_update();
4.       Add DMA operation in mmc.c
19        +#define SPI_DMA_ENABLE
20        +
21        +#ifdef SPI_DMA_ENABLE
22        +static uint32_t rx_ch, tx_ch;
23        +extern SI32_DMADESC_A_Type dma_desc[SI32_DMACTRL_NUM_CHANNELS + SI32_DMADESC_ALT_STRIDE];
24        +#endif
25        +#define SPI_SLOW_SPEED 100000   // 100K HZ
26        +#define SPI_HIGH_SPEED 800000   // 8M Hz
27         /* Port controls  (Platform dependent) */
32        +#define        FCLK_SLOW()        SI32_SPI_A_write_clkrate(SI32_SPI_0, APBCLK / (2 * SPI_SLOW_SPEED) -1) /* Set slow clock 100k Hz*/
33        +#define        FCLK_FAST()        SI32_SPI_A_write_clkrate(SI32_SPI_0, APBCLK / (2 * SPI_HIGH_SPEED) -1) /* Set fast clock 8M Hz */

135         static void xmit_spi_multi(const BYTE *p,UINT cnt)
136         {
137        +#ifdef SPI_DMA_ENABLE
138        +    uint32_t temp;
139        +    SI32_DMADESC_A_configure(&dma_desc[rx_ch],
140        +         SI32_SPI_0_RX_ENDPOINT, &temp, cnt / 4, SI32_DMADESC_A_CONFIG_WORD_PIPE);
141        +    SI32_DMADESC_A_configure(&dma_desc[tx_ch],
142        +        p, SI32_SPI_0_TX_ENDPOINT, cnt / 4, SI32_DMADESC_A_CONFIG_WORD_TX);
143        +
144        +    SI32_DMACTRL_A_enable_channel(SI32_DMACTRL_0, rx_ch);
145        +    SI32_DMACTRL_A_enable_channel(SI32_DMACTRL_0, tx_ch);
146        +
147        +    // 6. Run the complete peripheral DMA cycle
148        +    SI32_SPI_A_enable_dma_requests(SI32_SPI_0);
149        +
150        +    while(SI32_DMACTRL_A_read_chen(SI32_DMACTRL_0));
151        +#else
152        +
...
168         static void rcvr_spi_multi(BYTE *p,UINT cnt)
169         {
170        +#ifdef SPI_DMA_ENABLE
171        +    uint32_t temp;
172        +    SI32_DMADESC_A_configure(&dma_desc[rx_ch],
173        +         SI32_SPI_0_RX_ENDPOINT, p, cnt / 4, SI32_DMADESC_A_CONFIG_WORD_RX);
174        +    SI32_DMADESC_A_configure(&dma_desc[tx_ch],
175        +        &temp, SI32_SPI_0_TX_ENDPOINT, cnt / 4, SI32_DMADESC_A_CONFIG_WORD_PIPE);
176        +
177        +    SI32_DMACTRL_A_enable_channel(SI32_DMACTRL_0, rx_ch);
178        +    SI32_DMACTRL_A_enable_channel(SI32_DMACTRL_0, tx_ch);
179        +
180        +    // 6. Run the complete peripheral DMA cycle
181        +    SI32_SPI_A_enable_dma_requests(SI32_SPI_0);
182        +
183        +    while(SI32_DMACTRL_A_read_chen(SI32_DMACTRL_0));
184        +#else
185                 do {

5.       Source code

Apr 17, 2013

Three VCPs support



Jussipo has made USB split mode enabled and dual VCP work on his side. So I started looking into this issue, fixed two defects and make three VCPs work. Here is the summary.
1.     Enable USB split mode
In src\LUFA\Drivers\USB\Core\SIM3u\Endpoint_SIM3U.c
...        ...        @@ -54,15 +54,15 @@ bool Endpoint_ConfigureEndpoint(const uint8_t Address,
54        54        
55        55                 if(Number != ENDPOINT_CONTROLEP)
56        56                 {
57                -                ep_config = 0x450024; // bit 2,5,16,18,22 must be writen
58                -
57        +                ep_config = 0x450424;  // bit 2,5,10,16,18,22 must be written
58        +                                       //IURF,ISTSTLI,SPLITEN, OPRDYI,OORF,OSTSTLI
59        59                         if(Type == EP_TYPE_ISOCHRONOUS)
60        60                         {   // ISO transfer need set "1"
61        61                                 ep_config |= (SI32_USBEP_A_EPCONTROL_IISOEN_ISO_U32);
62        62                         }
63        63                         if(dir_sel)
64        64                         {   // IN endpoint
65                -                        ep_config |= (SI32_USBEP_A_EPCONTROL_DIRSEL_IN_U32|SI32_USBEP_A_EPCONTROL_ICLRDT_RESET_U32);
65        +            ep_config |= (SI32_USBEP_A_EPCONTROL_ICLRDT_RESET_U32);
66        66                                 SI32_USBEP_A_set_in_max_packet_size(USB_EPn(Number),Size>>3);
67        67                         }
68        68                         else

2.     Make a function to sent endpoint direction.
In src\LUFA\Drivers\USB\Core\SIM3u\Endpoint_SIM3U.h
...        ...        @@ -591,7 +591,11 @@
591        591                                 static inline void Endpoint_SetEndpointDirection(const uint32_t DirectionMask) ATTR_ALWAYS_INLINE;
592        592                                 static inline void Endpoint_SetEndpointDirection(const uint32_t DirectionMask)
593        593                                 {
594                -                                return;
594        +                if( DirectionMask == ENDPOINT_DIR_IN)
595        +                    USB_EPn(usb_ep_selected)->EPCONTROL.DIRSEL = 1;
596        +                else
597        +                    USB_EPn(usb_ep_selected)->EPCONTROL.DIRSEL = 0;
598        +                return;
595        599                                 }
3.  Add Endpoint_SetEndpointDirection() call in CDC send/receive function

In src\LUFA\Drivers\USB\Class\Devcie\CDCClassDevice.c
old        new        
...        ...        @@ -160,6 +160,7 @@ uint8_t CDC_Device_SendData(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo,
160        160                 if ((USB_DeviceState != DEVICE_STATE_Configured) || !(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS))
161        161                   return ENDPOINT_RWSTREAM_DeviceDisconnected;
162        162        
163        +        Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN);
163        164                 Endpoint_SelectEndpoint(CDCInterfaceInfo->Config.DataINEndpoint.Address);
164        165                 return Endpoint_Write_Stream_LE(Buffer, Length, NULL);
165        166         }
...        ...        @@ -249,6 +250,7 @@ int16_t CDC_Device_ReceiveByte(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInf
249        250        
250        251                 int16_t ReceivedByte = -1;
251        252        
253        +        Endpoint_SetEndpointDirection(ENDPOINT_DIR_OUT);
252        254                 Endpoint_SelectEndpoint(CDCInterfaceInfo->Config.DataOUTEndpoint.Address);
253        255        
254        256                 if (Endpoint_IsOUTReceived())
4.  Prepared three individual INF files for one, two, three VCPs
Main modifications are list below.
In src\VirtualSerial\
88        +[DeviceList]
89        +%DESCRIPTION%= DriverInstall,USB\VID_10C4&PID_A012&MI_00, USB\VID_10C4&PID_A012&MI_02
90        +
91        +[DeviceList.NTamd64]
92        +%DESCRIPTION%= DriverInstall,USB\VID_10C4&PID_A012&MI_00, USB\VID_10C4&PID_A012&MI_02

5.  Three VCP share same interrupt endpoint, and each BULK in/out endpoint number is same since we use USB split mode.
In src\VirtualSerial\Descriptors.h
...        ...        @@ -40,15 +40,33 @@
40        40                         #include
41        41        
42        42                 /* Macros: */
43        +        #define DUAL_VCP_ENABLE 1
44        +        #define TRI_VCP_ENABLE  0
45        +
43        46                         /** Endpoint address of the CDC device-to-host notification IN endpoint. */
44        47                         #define CDC_NOTIFICATION_EPADDR        (ENDPOINT_DIR_IN  | 1)
45        48        
46                -                /** Endpoint address of the CDC device-to-host data IN endpoint. */
47                -                #define CDC_TX_EPADDR                  (ENDPOINT_DIR_IN  | 2)
49        +        /** Endpoint address of the CDC1 device-to-host data IN endpoint. */
50        +                #define CDC1_TX_EPADDR                  (ENDPOINT_DIR_IN  | 2)
51        +
52        +                /** Endpoint address of the CDC1 host-to-device data OUT endpoint. */
53        +                #define CDC1_RX_EPADDR                  (ENDPOINT_DIR_OUT | 2)
54        +
55        +#if (DUAL_VCP_ENABLE || TRI_VCP_ENABLE)
56        +        /** Endpoint address of the CDC2 device-to-host data IN endpoint. */
57        +        #define CDC2_TX_EPADDR                  (ENDPOINT_DIR_IN  | 3)
58        +
59        +        /** Endpoint address of the CDC2 host-to-device data OUT endpoint. */
60        +        #define CDC2_RX_EPADDR                  (ENDPOINT_DIR_OUT | 3)
61        +#endif
48        62        
49                -                /** Endpoint address of the CDC host-to-device data OUT endpoint. */
50                -                #define CDC_RX_EPADDR                  (ENDPOINT_DIR_OUT | 3)
63        +#if (TRI_VCP_ENABLE)
64        +        /** Endpoint address of the CDC3 device-to-host data IN endpoint. */
65        +        #define CDC3_TX_EPADDR                  (ENDPOINT_DIR_IN  | 4)
51        66        
67        +        /** Endpoint address of the CDC3 host-to-device data OUT endpoint. */
68        +        #define CDC3_RX_EPADDR                  (ENDPOINT_DIR_OUT | 4)
69        +#endif

6.  Add IAD, second and third VCP descriptions.
In src\VirtualSerial\Descriptors.c
Here are part of modifications.
old        new        
...        ...        @@ -47,15 +47,21 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
47        47         {
48        48                 .Header                 = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},
49        49        
50                -        .USBSpecification       = VERSION_BCD(01.10),
51                -        .Class                  = CDC_CSCP_CDCClass,
52                -        .SubClass               = CDC_CSCP_NoSpecificSubclass,
53                -        .Protocol               = CDC_CSCP_NoSpecificProtocol,
50        +    .USBSpecification       = VERSION_BCD(02.00),
51        +    .Class                  = USB_CSCP_IADDeviceClass,
52        +    .SubClass               = USB_CSCP_IADDeviceSubclass,
53        +    .Protocol               = USB_CSCP_IADDeviceProtocol,
54        54        
7.  Made other VCP works in main loop.
In src\VirtualSerial\VirtualSerial.c
87        150        
88        151         void VCOM_echo(void)
89        152         {
90                -        if(CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface))
153        +        if(CDC_Device_BytesReceived(&VirtualSerial_CDC1_Interface))
91        154                 {
92                -                in_buff[0] = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
93                -                CDC_Device_SendData(&VirtualSerial_CDC_Interface, (char *)in_buff, 1);
155        +                in_buff[0] = CDC_Device_ReceiveByte(&VirtualSerial_CDC1_Interface);
156        +                CDC_Device_SendData(&VirtualSerial_CDC1_Interface, (char *)in_buff, 1);
94        157                         Endpoint_ClearIN();
95        158                 }
159        +#if (DUAL_VCP_ENABLE || TRI_VCP_ENABLE)
160        +        if(CDC_Device_BytesReceived(&VirtualSerial_CDC2_Interface))
161        +        {
162        +                in2_buff[0] = CDC_Device_ReceiveByte(&VirtualSerial_CDC2_Interface);
163        +                CDC_Device_SendData(&VirtualSerial_CDC2_Interface, (char *)in2_buff, 1);
164        +                Endpoint_ClearIN();
165        +        }
166        +#endif
167        +#if (TRI_VCP_ENABLE)
168        +    if(CDC_Device_BytesReceived(&VirtualSerial_CDC3_Interface))
169        +    {
170        +        in3_buff[0] = CDC_Device_ReceiveByte(&VirtualSerial_CDC3_Interface);
171        +        CDC_Device_SendData(&VirtualSerial_CDC3_Interface, (char *)in3_buff, 1);
172        +        Endpoint_ClearIN();
173        +    }
174        +#endif
96        175         }

8.  Fix defect on handling zero package issue.
In src\LUFA\Drivers\USB\Core\SIM3u\EndpointStream_SIM3U.c
...        ...        @@ -265,6 +265,13 @@ uint8_t Endpoint_Write_Stream_LE(const void* const Buffer,
265        265                                 {
266        266                                         count = USB0_EP0_write_fifo(buf, Length);
267        267                                 }
268        +                        if(Length == ENDPOINT_CONTROLEP_DEFAULT_SIZE)
269        +                        {
270        +                                SI32_USB_A_set_in_packet_ready_ep0(SI32_USB_0);
271        +                                if ((ErrorCode = Endpoint_WaitUntilReady()) != ENDPOINT_READYWAIT_NoError)
272        +                                  return ErrorCode;
273        +                                SI32_USB_A_set_in_packet_ready_ep0(SI32_USB_0);
274        +                        }
9.  Fixed usb_ep_selected defect in USB ISR.
In src\LUFA\Drivers\USB\Core\SIM3u\USBInterrupt_SIM3U.c
...        ...        @@ -103,7 +103,7 @@ void USB0_IRQHandler(void)
103        103                   USB0_ep0_handler();
104        104                   return;
105        105           }
106                -
106        +  uint32_t ep_selected_backup = usb_ep_selected;
107        107           if (usbEpInterruptMask & (SI32_USB_A_IOINT_IN1I_MASK | SI32_USB_A_IOINT_OUT1I_MASK))
108        108           {
109        109                   Endpoint_SelectEndpoint(1);
...        ...        @@ -124,7 +124,7 @@ void USB0_IRQHandler(void)
124        124                   Endpoint_SelectEndpoint(4);
125        125                   USB0_epn_handler();
126        126           }
127                -
127        +  usb_ep_selected = ep_selected_backup;
128        128           // Handle Start of Frame Interrupt
10.          Source code