This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

C6748's OHCI-compliant USB port (USB1).

Other Parts Discussed in Thread: OMAP-L138

Hi,

I've been spending time writing code for the OHCI-compliant USB peripheral on the C6748 and am experiencing some problems.

Basically, I've set up the HCD pretty much as OpenHCI dictates. I successfully detect when a device attaches, wait 100ms, reset the device and then I try and get the (first eight bytes of the) device descriptor. Now this is where I am not having much success.

I've set up the endpoint descriptors list for the control list to do, by default, a GET_DESCRIPTOR request with a length of 8 bytes. To see what the state of shared memory is, please first see my debug output:


ControlList is now:
 |
ED 0xc23240f0 -> GTD 0xc00937f0 -> GTD 0xc2324240 -> GTD 0xc232b740 -> GTD 0xc232b750 (dummy)
 
ED 0xc23240f0:
    FunctionAddress    : 0
    EndpointNumber     : 0
    Direction          : 0
    sKip               : 0
    Format             : 0
    MaximumPacketSize  : 8
    TDQueueTailPointer : 0xc232b750
    Halted             : 0
    toggleCarry        : 0
    TDQueueHeadPointer : 0xc00937f0
    NextED             : 0x0
 
    GTD 0xc00937f0:
        BufferRounding       : 1
        Direction            : 0
        DelayInterrupt       : 0
        DataToggle           : 2
        ErrorCount           : 0
        ConditionCode        : 0xf
        CurrentBufferPointer : 0xc2324168
        NextTD               : 0xc2324240
        BufferEnd            : 0xc232416f
 
    GTD 0xc2324240:
        BufferRounding       : 1
        Direction            : 2
        DelayInterrupt       : 0
        DataToggle           : 3
        ErrorCount           : 0
        ConditionCode        : 0xf
        CurrentBufferPointer : 0xc23241b0
        NextTD               : 0xc232b740
        BufferEnd            : 0xc23241b7
 
    GTD 0xc232b740:
        BufferRounding       : 0
        Direction            : 1
        DelayInterrupt       : 0
        DataToggle           : 3
        ErrorCount           : 0
        ConditionCode        : 0xf
        CurrentBufferPointer : 0x0
        NextTD               : 0xc232b750
        BufferEnd            : 0x0
 
    GTD 0xc232b750 (dummy):
        BufferRounding       : 0
        Direction            : 0
        DelayInterrupt       : 0
        DataToggle           : 0
        ErrorCount           : 0
        ConditionCode        : 0x0
        CurrentBufferPointer : 0x0
        NextTD               : 0x0
        BufferEnd            : 0x0

For anyone interested, here's some actual memory that relates to these values ...


0xc00937f0: 0xF2040000 0xC2324168 0xC2324240 0xC232416F
0xc23240f0: 0x00080000 0xC232B750 0xC00937F0 0x00000000
0xc2324240: 0xF3140000 0xC23241B0 0xC232B740 0xC23241B7
0xc232b740: 0xF3080000 0x00000000 0xC232B750 0x00000000
0xc232b750: 0x00000000 0x00000000 0x00000000 0x00000000

... and heres some of the C6748's USB1 registers at the same time instance ...


USB1DSP
 REVID = 0x00000010
  REV = 0x10
 HcControl = 0x00000280
  Reserved = 0x0
  RWE = DISABLE (0x0)
  RWC = ENABLE (0x1)
  IR = *
  HCFS = USBOPERATIONAL (0x2)
  BLE = DISABLE (0x0)
  CLE = DISABLE (0x0)
  IE = DISABLE (0x0)
  PLE = DISABLE (0x0)
  CBSR = RATIO11 (0x0)
 HcCommandStatus = 0x00000000
  Reserved = 0x0
  SOC = 0x0
  Reserved = 0x0
  OCR = 0x0
  BLF = CLEAR (0x0)
  CLF = CLEAR (0x0)
  HCR = CLEAR (0x0)
 HcInterruptStatus = 0x00000004
  Rsvd = 0x0
  OC = 0x0
  Reserved = 0x0
  RHSC = 0x0
  FNO = 0x0
  UE = 0x0
  RD = 0x0
  SF = 0x1
  WDH = 0x0
  SO = 0x0
 HcInterruptEnable = 0x8000007B
  MIE = ENABLE (0x1)
  OC = IGNORE (0x0)
  Reserved = 0x0
  RHSC = ENABLE (0x1)
  FNO = ENABLE (0x1)
  UE = ENABLE (0x1)
  RD = ENABLE (0x1)
  SF = IGNORE (0x0)
  WDH = ENABLE (0x1)
  SO = ENABLE (0x1)
 HcInterruptDisable = 0x8000007B
  MIE = DISABLE (0x1)
  OC = IGNORE (0x0)
  Reserved = 0x0
  RHSC = DISABLE (0x1)
  FNO = DISABLE (0x1)
  UE = DISABLE (0x1)
  RD = DISABLE (0x1)
  SF = IGNORE (0x0)
  WDH = DISABLE (0x1)
  SO = DISABLE (0x1)
 HcHCCA = 0xC234D600
  HCCA = 0xC234D6
  Reserved = 0x0
 HcPeriodCurrentED = 0x00000000
  PCED = 0x0
  Reserved = 0x0
 HcControlHeadED = 0xC23240F0
  CHED = 0xC23240F
  Reserved = 0x0
 HcControlCurrentED = 0x00000000
  CCED = 0x0
  Reserved = 0x0
 HcBulkHeadED = 0x00000000
  BHED = 0x0
  Reserved = 0x0
 HcBulkCurrentED = 0x00000000
  BCED = 0x0
  Reserved = 0x0
 HcDoneHead = 0x00000000
  DH = 0x0
  Reserved = 0x0
 HcFmInterval = 0x27782EDF
  FIT = 0x0
  FSMPS = 0x13BC
  Reserved = 0x0
  FrameInterval = 0x2EDF
 HcFmRemaining = 0x0000209F
  FRT = 0x0
  Reserved = 0x0
  FR = 0x209F
 HcFmNumber = 0x00007E2E
  Reserved = 0x0
  FN = 0x7E2E
 HcPeriodicStart = 0x00002A30
  Reserved = 0x0
  PS = 0x2A30
 HcLSThreshold = 0x00000628
  Reserved = 0x0
  LST = 0x628
 HcRhDescriptorA = 0x02001202
  POTPGT = 0x2
  Reserved = 0x0
  NOCP = DISABLE (0x1)
  OCPM = GLOBAL (0x0)
  DT = 0x0
  NPS = ALWAYSON (0x1)
  PSM = GLOBAL (0x0)
  NDP = 0x2
 HcRhDescriptorB = 0x00000000
  PPCM = P1GPDIS (0x0)
  DR = P1DR (0x0)
 HcRhStatus = 0x00000000
  CRWE = *
  Reserved = 0x0
  OCIC = NOEFFECT (0x0)
  LPSC = NOEFFECT (0x0)
  DRWE = NOEFFECT (0x0)
  Reserved = 0x0
  OCI = 0x0
  LPS = NOEFFECT (0x0)
 HcRhPortStatus = 0x00000101
  Reserved = 0x0
  PRSC = NOEFFECT (0x0)
  OCIC = NOEFFECT (0x0)
  PSSC = NOEFFECT (0x0)
  PESC = NOEFFECT (0x0)
  CSC = NOEFFECT (0x0)
  Reserved = 0x0
  LSDA = NOEFFECT (0x0)
  PPS = PPSSET (0x1)
  Reserved = 0x0
  PRS = NOEFFECT (0x0)
  POCI = NOEFFECT (0x0)
  PSS = NOEFFECT (0x0)
  PES = NOEFFECT (0x0)
  CCS = CLEAR (0x1)

This is more or less correct -- at least from my interpretation of the OHCI 1.0a specification.

I then set CLF (CommandListFilled) in HhCommandStatus and CLE (CommandListEnabled) in HcControl to try and get the HC (i.e. C6748's integrated OHCI-compliant PHY for USB1) to do the actual work and transfer some data.

The problem is that I don't always get a response. Most of the time I do not get an WritebackDoneHead interrupt, and if I do, it is to tell me that the first of the transfers (the SETUP one) failed and the endpoint is now halted. A lot of the time, however, I do *not* get a WritebackDoneHead interrupt and nothing else happens. If I halt the program and manually set CLF then usually I do get an interrupt saying the first transfer failed and the endpoint is now halted.

Whether or not I get an interrupt in the first place seems to depend on seemingly unrelated things, such as including debug output in the program. Could I be missing some timeouts somewhere? I currently timeout between connection and reset (100ms), and a little while after reset (100ms).

I also do not always understand how the HC seems to modify the transfer descriptor lists. A lot of the time, the HC sets the NextTransferDescriptorPtr field of a transfer descriptor to 0x00000000 (I guess this is correct as HcDoneHead is 0x00000000 to start with) but then does not update the accompanying endpoint descriptors TransferDescriptorQueueHeadPtr.

If any one is able to help me make some progress with my firmware, it'd be greatly appreciated.

Kind regards,

Mark

  • Hi,

    -- Note I've edited this post to reflect some recent findings.

    I have some information to add to my query.

    To see what is going on at a lower level I've attached a oscilloscope to the D+ line. When I plug in a device, as expected, the D+ is pulled up. Then, after a certain amount of time, I perform a reset in code:


    // Reset the port.
    CSL_FINS(USB1_REGS_PTR->HCRHPORTSTATUS, OHCI_HCRHPORTSTATUS_PRS, CSL_OHCI_HCRHPORTSTATUS_PRS_PORTRESET);

    The reset pulls the D+ down for about 10ms and then, whilst I try and control the HC, the D+ line goes back to being high and remains there indefinitely.

    Now, interestingly, if I put a breakpoint straight after the reset above, I can see USB frames on the D+ wire! However, after a seemingly random point in time a few instructions after the reset, the D+ goes high and remains high indefinitely.

    I've changed my code to include a mundane infinite loop after the reset (to ensure it isn't a particular line of code that makes the signal die) as below:


    // Reset the port.
    CSL_FINS(USB1_REGS_PTR->HCRHPORTSTATUS, OHCI_HCRHPORTSTATUS_PRS, CSL_OHCI_HCRHPORTSTATUS_PRS_PORTRESET);
     
    uint64_t counter = 0;
     
    while (true)
    {
        counter++;
    }

    And normally on the second iteration of the while loop the signal disappears.

    It turns out that the signal disappears when I get an USB1 interrupt where the HcRhPortStatus bits have PortResetStatusChange as 1 and PortReset as 0. The line I do to change the status change bit is:


    USB1_REGS_PTR->HCRHPORTSTATUS |= CSL_OHCI_HCRHPORTSTATUS_PRSC_MASK;

    Which immediately kills the signal. From the description on OHCI or TI's description of the register, all that should happen on this line of code is that the PortResetStatusChange bit is cleared.

    I would be very keen to learn why I am observing these issues. If I do not clear the PortResetStatusChange bit I see some interesting signals when I set up some endpoint/transfer descriptors but, unfortunately, the endpoint still halts and the transfers still do not complete. I get many (~15) WritebackDoneHead interrupts, most of which when HccaDoneHead is 0x00000000, and even though I only have set up three transfer descriptors. I would be very keep to find out why this is the case.

    Any thoughts on this are much appreciated.

    Kind regards,

    Mark

  • Mark K said:

    USB1_REGS_PTR->HCRHPORTSTATUS |= CSL_OHCI_HCRHPORTSTATUS_PRSC_MASK;

    From using the CCS debugger it seems that on this line, as well as the status change being cleared, the PES bit in HcRhPortStatus is cleared as well, disabling the port.

    I do not understand why this happens. Is it possible for someone from TI to elaborate on why this might happen?

    Kind regards,

    Mark

  • Hi,

    If it helps in answering this question, I can confirm that the signals I see between a reset and between setting the PSRC bit are *exclusively* SOF packets. They are sent every 1ms as the specification dictates.

    Kind regards,

    Mark

  • Hi,

    I seem to have solved at least one of my problems above.

    The first problem was that the communications stopped at a certain point. The problem with the line


    USB1_REGS_PTR->HCRHPORTSTATUS |= CSL_OHCI_HCRHPORTSTATUS_PRSC_MASK;

    was that if the CCS bit is set then by or'ing it here the CCS bit is written to HCRHPORTSTATUS. This, as a side-effect, clears PES. Changing the line to


    USB1_REGS_PTR->HCRHPORTSTATUS = CSL_OHCI_HCRHPORTSTATUS_PRSC_MASK;

    solved that problem. My second problem was that usually the HccaDoneHead pointer was 0x00000000. This problem persists to some extend. However I have been experimenting with this and found that if I place HccaDoneHead on a heap in internal memory (as opposed to external memory) it does not return null pointers!

    However this is directly against TI's own specifications which state (sprufm8): "These data
    structures and data buffers can be located in internal or external system RAM".

    Hence, could someone please clarify precisely the requirements/assumptions TI makes on the HCCA memory region or the various descriptors. I would like to know whether/how it should work in internal or external memory, whether there are special things I need to do to enforce cache coherency, etc.

    Kind regards,

    Mark

     

  • Hi Mark

    Mark K said:
    My second problem was that usually the HccaDoneHead pointer was 0x00000000. This problem persists to some extend. However I have been experimenting with this and found that if I place HccaDoneHead on a heap in internal memory (as opposed to external memory) it does not return null pointers!

    Is this the only outstanding issue on this thread? Please confirm

    Does this work if you use external memory? When you try to use the internal memory, can you share the exact memory and address that you are trying to use? Maybe a snapshot of the relevant code and your linker command file would also help.

    Regards

    Mukul

     

  • Dear Mukul,

    Yes I suppose this is the only outstanding issue.

    It does not work with external memory, only with internal memory. As the rest of my application uses exclusively DRAM and the OHCI user guide seems to suggest external memory is OK, I was hoping to keep it that way.

    An actual address allocated for HCCA in a heap in external memory was 0xC0000500. Note that I have taken care to align the various datastructures as specified in the OpenHCI 1.0a.

    I would be happy to send code and configuration files but not on the forum (please email me or provide me with an email address).

    Kind regards,

    Mark

  • Dear Mukul,

    If it helps, here is my TCF file. The OHCI peripheral fails if I allocate the various data structures in the heap in DDR.


    utils.loadPlatform("ti.platforms.evm6748");
     
    /* Enable real-time LOG.  */
    bios.enableRealTimeAnalysis(prog);
     
    // Enable heaps.
    bios.enableMemoryHeaps(prog);
     
    /* Enable real-time exchange of raw logs with CCS. */
    bios.enableRtdx(prog);
     
    // Enable tasks.
    bios.enableTskManager(prog);
     
    // Stack size for bios/main/isrs, etc.
    bios.MEM.STACKSIZE = 0x2000;
     
    // Create a heap in external RAM.
    bios.MEM.instance("DDR").base = 0xc0000000;
    bios.MEM.instance("DDR").len = 0x08000000;
    bios.MEM.instance("DDR").createHeap = 1;
    bios.MEM.instance("DDR").enableHeapLabel = true;
    bios.MEM.instance("DDR")["heapLabel"] = prog.extern("SEG0");
    bios.MEM.instance("DDR").heapSize = 0x04000000;
    bios.MEM.instance("DDR").space = "code/data";
     
    // Use first 128K of SDRAM as "IRAM".
    bios.MEM.instance("IRAM").base = 0x11800000;
    bios.MEM.instance("IRAM").len = 0x00020000;
    bios.MEM.instance("IRAM").createHeap = 0;
    bios.MEM.instance("IRAM").space = "code/data";
     
    // Use last 128K of SDRAM for L2-cache.
    bios.MEM.instance("CACHE_L2").base = 0x11820000;
    bios.MEM.instance("CACHE_L2").len = 0x00020000;
     
    // Use run-time support library without exceptions.
    bios.GBL.SPECIFYRTSLIB = 1;
    bios.GBL.RTSLIB = "rts6740.lib";
     
    // Disable tracing.
    bios.GBL.ENABLEALLTRC = 1;
     
    // Enable L2-cache of size 128K.
    bios.GBL.C64PLUSL2CFG = "128k";
     
    // Enable caching of external RAM (0xc0000000 - 0xc8000000)
    bios.GBL.C64PLUSCONFIGURE = 1;
    bios.GBL.C64PLUSMAR192to223 = 0x000000ff;
     
    // Configure kernel thread via IDLE task.
    // Setup the idle task
    bios.TSK.instance("TSK_idle").allocateTaskName = 1;
    bios.TSK.instance("TSK_idle").stackSize = 0x1000;
    bios.TSK.instance("TSK_idle").stackMemSeg = prog.get("DDR");
     
    bios.TSK.create("TSK_main");
    bios.TSK.instance("TSK_main").allocateTaskName = 1;
    bios.TSK.instance("TSK_main").stackSize = 0x4000;
    bios.TSK.instance("TSK_main").stackMemSeg = prog.get("DDR");
    bios.TSK.instance("TSK_main").priority = 5;
    bios.TSK.instance("TSK_main").order = 1;
    bios.TSK.instance("TSK_main").comment = "main activity of the DSP Firmware";
    bios.TSK.instance("TSK_main").fxn = prog.extern("taskMainFunction");
     
    bios.IDL.instance("LNK_dataPump").order = 1;
    bios.IDL.instance("RTA_dispatcher").order = 2;
    bios.IDL.instance("IDL_cpuLoad").order = 3;
    bios.IDL.instance("PWRM_idleCPU").order = 4;
     
    bios.TSK.CALLSWITCHFXN = 1;
    bios.TSK.SWITCHFXN = prog.extern("tskSwitchFxn");
     
    // Configure hardware interrupts.
    bios.HWI.instance("HWI_INT5").interruptSelectNumber = 19;
    bios.HWI.instance("HWI_INT5").fxn = prog.extern("serviceUSB0Interrupt");
    bios.HWI.instance("HWI_INT5").useDispatcher = 1;
     
    // Configure logs.
    bios.LOG.instance("LOG_system").bufLen = 128;
    bios.LOG.instance("LOG_system").bufSeg = prog.get("DDR");
     
    bios.LOG.create("trace");
    bios.LOG.instance("trace").bufLen = 256;
    bios.LOG.instance("trace").bufSeg = prog.get("DDR");
     
    bios.LOG.create("monitorLog");
    bios.LOG.instance("monitorLog").bufLen = 256;
    bios.LOG.instance("monitorLog").bufSeg = prog.get("DDR");
     
    bios.LOG.create("applicationLog");
    bios.LOG.instance("applicationLog").bufLen = 256;
    bios.LOG.instance("applicationLog").bufSeg = prog.get("DDR");
     
    bios.LOG.create("usbCommsLog");
    bios.LOG.instance("usbCommsLog").bufLen = 1024;
    bios.LOG.instance("usbCommsLog").bufSeg = prog.get("DDR");
     
    bios.LOG.create("mmcsdLog");
    bios.LOG.instance("mmcsdLog").bufLen = 64;
    bios.LOG.instance("mmcsdLog").bufSeg = prog.get("DDR");
     
    bios.LOG.create("adcLog");
    bios.LOG.instance("adcLog").bufLen = 1024;
    bios.LOG.instance("adcLog").bufSeg = prog.get("DDR");
     
    bios.LOG.create("filesystemLog");
    bios.LOG.instance("filesystemLog").bufLen = 64;
    bios.LOG.instance("filesystemLog").bufSeg = prog.get("DDR");
     
    bios.LOG.create("applicationLayerLog");
    bios.LOG.instance("applicationLayerLog").bufLen = 64;
    bios.LOG.instance("applicationLayerLog").bufSeg = prog.get("DDR");
     
    bios.LOG.create("spiLog");
    bios.LOG.instance("spiLog").bufLen = 64;
    bios.LOG.instance("spiLog").bufSeg = prog.get("DDR");
     
    // Allocate everything in external RAM instead of internal RAM.
    bios.MEM.BIOSOBJSEG = prog.get("DDR");
    bios.MEM.MALLOCSEG = prog.get("DDR");
     
    bios.MEM.ARGSSEG = prog.get("DDR");
    bios.MEM.STACKSEG = prog.get("DDR");
    bios.MEM.GBLINITSEG = prog.get("DDR");
    bios.MEM.TRCDATASEG = prog.get("DDR");
    bios.MEM.SYSDATASEG = prog.get("DDR");
    bios.MEM.OBJSEG = prog.get("DDR");
    bios.MEM.BIOSSEG = prog.get("DDR");
    bios.MEM.SYSINITSEG = prog.get("DDR");
    bios.MEM.HWISEG = prog.get("DDR");
    bios.MEM.HWIVECSEG = prog.get("DDR");
    bios.MEM.RTDXTEXTSEG = prog.get("DDR");
    bios.MEM.USERCOMMANDFILE = 0;
    bios.MEM.TEXTSEG = prog.get("DDR");
    bios.MEM.SWITCHSEG = prog.get("DDR");
    bios.MEM.BSSSEG = prog.get("DDR");
    bios.MEM.FARSEG = prog.get("DDR");
    bios.MEM.CINITSEG = prog.get("DDR");
    bios.MEM.PINITSEG = prog.get("DDR");
    bios.MEM.CONSTSEG = prog.get("DDR");
    bios.MEM.DATASEG = prog.get("DDR");
    bios.MEM.CIOSEG = prog.get("DDR");
     
    // Use external RAM instead of internal RAM everywhere.
    bios.BUF.OBJMEMSEG = prog.get("DDR");
    bios.SYS.TRACESEG = prog.get("DDR");
    bios.SEM.OBJMEMSEG = prog.get("DDR");
    bios.MBX.OBJMEMSEG = prog.get("DDR");
    bios.QUE.OBJMEMSEG = prog.get("DDR");
    bios.LCK.OBJMEMSEG = prog.get("DDR");
    bios.DIO.OBJMEMSEG = prog.get("DDR");
    bios.DHL.OBJMEMSEG = prog.get("DDR");
    bios.RTDX.RTDXDATASEG = prog.get("IRAM");
    bios.HST.OBJMEMSEG = prog.get("DDR");
    bios.HST.instance("RTA_fromHost").bufSeg = prog.get("DDR");
    bios.HST.instance("RTA_toHost").bufSeg = prog.get("DDR");
    bios.PIP.OBJMEMSEG = prog.get("DDR");
    bios.SIO.OBJMEMSEG = prog.get("DDR");
    bios.TSK.OBJMEMSEG = prog.get("DDR");
    bios.PRD.OBJMEMSEG = prog.get("DDR");
    bios.LOG.OBJMEMSEG = prog.get("DDR");
    bios.STS.OBJMEMSEG = prog.get("DDR");
    bios.TSK.STACKSEG = prog.get("DDR");
    bios.IDL.OBJMEMSEG = prog.get("DDR");
    bios.SWI.OBJMEMSEG = prog.get("DDR");
    bios.CLK.OBJMEMSEG = prog.get("DDR");
     
    bios.HWI.instance("HWI_INT7").interruptSelectNumber = 0;
    bios.HWI.instance("HWI_INT8").interruptSelectNumber = 1;
    bios.HWI.instance("HWI_INT9").interruptSelectNumber = 2;
    bios.HWI.instance("HWI_INT10").interruptSelectNumber = 3;
     
    bios.HWI.instance("HWI_INT7").arg = 0;
    bios.HWI.instance("HWI_INT8").arg = 1;
    bios.HWI.instance("HWI_INT9").arg = 2;
    bios.HWI.instance("HWI_INT10").arg = 3;
     
    // Enable event chaining (for PSP drivers)
    bios.ECM.ENABLE = 1;
     
    // Enable power management.
    bios.PWRM.ENABLE = 1;
    bios.PWRM.ONCHIPMEMSEG = prog.get("IRAM");
    bios.PWRM.RESOURCETRACKING = 1;
    bios.PWRM.SCALING = 1;
    bios.PWRM.MOVESPS = 1;
     
    bios.PWRM.ENABLE = 0;
     
    bios.HWI.instance("HWI_INT6").interruptSelectNumber = 20;
    bios.HWI.instance("HWI_INT6").fxn = prog.extern("serviceUsb1Interrupt");
    bios.HWI.instance("HWI_INT5").fxn = prog.extern("serviceUsb0Interrupt");
    bios.HWI.instance("HWI_INT6").useDispatcher = 1;
    bios.LOG.instance("usbCommsLog").destroy();
    bios.LOG.create("usbCommsLog");
    bios.LOG.instance("usbCommsLog").comment = "<add comments here>";
    bios.LOG.instance("usbCommsLog").bufLen = 1024;
    bios.LOG.instance("usbCommsLog").bufSeg = prog.get("DDR");
    bios.LOG.instance("usbCommsLog").dataType = "printf";
    bios.LOG.instance("usbCommsLog").iId = 0;
    bios.LOG.instance("usbCommsLog").logType = "circular";
    bios.LOG.instance("usbCommsLog").destroy();
    bios.LOG.create("usbOtgLog");
    bios.LOG.instance("usbOtgLog").bufLen = 1024;
    bios.LOG.create("usbOhciLog");
    bios.LOG.instance("usbOhciLog").bufLen = 1024;
    // !GRAPHICAL_CONFIG_TOOL_SCRIPT_INSERT_POINT!
    prog.gen();

    Kind regards,

    Mark

  • Hi,

    Could you confirm whether sillicon errata 2.0.17 could be the culprit here. I *do* have mixed cache/data but I don't know whether OHCI employs SDMA or some other mechanism.

    Kind regards,

    Mark

  • Mark

    Jason/Anthony from TI field team have reported this issue to us, I believe some code was also shared, if it is not the complete code feel free to send it via Jason. Please do note that my primary resource for USB1.1 is out till middle of next week so the support is likely going to be limited. I regret the delay in our responses.

    Back to your query on the errata, my gut feel says that it should not be due to the errata , but to further investigate, is it posslble for you to use Shared RAM instead of L2 for the USB accesses, and test if that works? Alternatively is it possible for you to configure L2 as all SRAM and see if that works (it could degrade the performance but should be quick test to isolate this further?).

    Regards

    Mukul

  • Hi Mukul,

    I am happy to forward the code to you but would prefer to not do this via the forum. If you message me your email address or get my email address from Jason, I will send code this way.

    To clarify:

    With L2 being all cache, allocating USB stuff in DRAM, it doesn't work.

    With L2 being cache and RAM, allocatating USB stuff on the L2 heap, it does work.

    However, I do not want a heap in L2 and the specification says I don't need one; I'd like to find out what is going on.

    Kind regards,

    Mark

  • To rule out the advisory

    1) Can you try allocating USB stuff to Shared RAM instead of L2?

    2) Can you configure all of L2 as RAM (no cache) and see if that works?

    Regards

    Mukul

  • I admittedly don't know anything about the internals of an OHCI controller, but this smells an awful lot like a cache coherence issue.  That said:

    • All buffers that are accessed by both the CPU and the OHCI controller must be aligned on a 128 byte boundary.
    • The buffer must not contain any other data (e.g. if it was 100 bytes of data you wouldn't want random other data to be allocated within the same 128 bytes).
    • After the CPU writes to these memory locations a block writeback operation should be initiated in the L2 cache controller.
    • Before the CPU reads from these memory location a block invalidate operation should be initiated in the L2 cache controller.

     

  • Hi Brad,

    Thanks for your reply.

    I appreciate cache coherence could be at fault here but from my point of view these requirements are new; I read all the relevant documentation and there was no mention of this whatsoever! Would you be able to comment as to what these block writeback and block invalidate operations look like in terms of C/C++ statements?

    I would also like to understand why I need to implement these requirements, where I can read up about them, and whether there are other ways to solve my problem (perhaps I can use a non-cached heap in DRAM?). Also, it isn't quite clear to me whether you are sure that these requirements need to be adhered to (without having to know the internals of the OHCI controller) or whether these are requirements that may or may not apply to the OHCI controller. If it is the former, this definitely explains the issues I am having.

    Kind regards,

    Mark

  • I don't know if it's your only problem, but I can tell you with certainty that these cache coherence operations are required.  Cache coherence is "a cache thing" not a USB thing.  I know nothing about the OHCI, but I know a lot about the cache!  (It would be nice to give you some kind of hint of this in the OHCI guide, so I appreciate where you're coming from.)

    The c6748 has an interconnect called the Switched Central Resource.  Any master on that bus capable of initiating a transfer is generically called a "DMA".  So in particular, the "DMA"s in the system (in addition to the CPU and EDMA) are the PRU, Ethernet MAC, SATA, UPP, USB 2.0, USB 1.1, LCD Controller, and HPI.  Any time that the CPU and any other DMA share a buffer in external memory the considerations I listed are in play.  Cache coherence is described in Appendix A of the Cache User's Guide if you want to read more.  For details on the cache writeback/invalidate operations see the 674x Megamodule Reference Guide.  Section 4.3.6.2 "Block Coherence Operations" discusses the specifics.  A code example is given that shows how to do it.  For the case of the invalidate make sure you implement the optional spin loop before proceeding to read from external memory.

  • Hi Mark,

    To quickly start with Cache on C674x:
    Some workshop material are available with some lab exercises to show some cache coherance operation.
    See chap 11 of the Student guide v5.93.pdf:
    http://processors.wiki.ti.com/index.php/C6000_Embedded_Design_Workshop_Using_BIOS#Rev_5.93_IS_NOW_AVAILABLE
    The labs are meant to run on a OMAP-L138/C6748 EVM.

    Best regards,

    Anthony

  • Hi,

    I've decided that to move forward with development, I will for now allocate the data structures in internal memory. However, once development of the USB component has finished, I think I may have to use external memory and take into account these cache coherence issues.

    The thing I do not quite understand is the following. The buffers I pass to the OHCI are relatively small (to match the attached device's maximum endpoint size). That is, a large buffer to be read/written via USB is broken up into multiple little buffers and passed to the OHCI controller in the form of transfer descriptors. Now I am wondering the following: should each broken-up buffer be aligned to a 128 byte address, or is it sufficient for the large buffer to be aligned. If it is the former this could introduce a lot of overhead!

    Kind regards,

    Mark

     

  • Mark,

    Mark K said:
    The buffers I pass to the OHCI are relatively small (to match the attached device's maximum endpoint size). That is, a large buffer to be read/written via USB is broken up into multiple little buffers and passed to the OHCI controller in the form of transfer descriptors.

    How big are those little buffers (transfer descriptor)?

    Mark K said:
    Now I am wondering the following: should each broken-up buffer be aligned to a 128 byte address, or is it sufficient for the large buffer to be aligned. If it is the former this could introduce a lot of overhead!

    If you use internal memory for now then there a no cache alignement required has only external memory is cacheable.

    If you use external memory:
    a) Using a big buffer can be dangerous in case both read and write operation will occur on the descriptors (ie small portion of the big buffer).

    - Imagine that 2 descriptors span over the same cache line (for example descriptor A finishes on byte 120 of the cache line and descriptor B starts on byte 121of the cache line) then if A has been written by CPU and B has been read by CPU then you might corrupt one of the 2 descriptor depending on what will be the next coherancy operation you will perform.
    If you look at page 300 (of the Student guide v5.93.pdf mentioned on the previous post) this happen if part of the RCV buffer and TX buffer would share a cache line.

    So to use a big buffer for both read/write will force you to do coherancy operation after each read or write that occured (either on the whole buffer which will hit the perfromances or on part of the buffer but that requires mecanims to tracks what descriptor is where).

    b) An option would be to use a buffer for read only and one for write only. But even here you would need to be careful with synchronization and coherancy if descriptor A and B are coming to memory at different point in time and if their life time is different.

    c) Also in term of computation and performance it is better to have data (like descriptors) aligned on addresses that are multiple of the internal bus size. It ensures that CPU/EDMA and peripherals can make the fastest possible access. For example when CPU reads descriptor B above it will fetch the complete cache line. So only 8 bytes out of 128 are of interest.

    Hope it helps.

    Anthony

  • So what size are these descriptors and how many will there be?  My first impression is to just waste some external memory (DDR2 is cheap and large!) and do things safely.  Just to be clear, you need to do more than simply align the descriptors to 128 byte boundaries.  You also need something to occupy the rest of the line such that the linker doesn't allocate something else in that space.  I assume the descriptor is a structure, so you just need to add a character array to fill up the rest of the line.

    You can make your programming a little easier by using STRUCT_ALIGN (instead of DATA_ALIGN).  The STRUCT_ALIGN pragma can be applied to a type whereas DATA_ALIGN is applied to an instance.  Here's an example to demonstrate:

    #ifdef USB_DESCRIPTOR_EXTERNAL
    #pragma STRUCT_ALIGN(USB_DESCR, 128);
    #endif
    typedef struct usb_descr
    {
       unsigned int word1; // 4 bytes
       unsigned int word2; // 8 bytes
       unsigned int word3; // 12 bytes
       unsigned int word4; // 16 bytes
    #ifdef USB_DESCRIPTOR_EXTERNAL
       unsigned char linefill[112]; // 128 bytes
    #endif
    } USB_DESCR;

    You could do something similar in your code that actually handles those USB descriptors.  For example:

    #ifdef USB_DESCRIPTOR_EXTERNAL
       // only necessary if you are going to perform a read from a descriptor
       // can be skipped if you are only doing writes to the descriptor
       BCACHE_inv((Ptr)hDescriptor, sizeof(hDescriptor), 1 /* wait */);
    #endif

       // Read descriptor, do whatever it is you're doing
       // Write  new descriptor

    #ifdef USB_DESCRIPTOR_EXTERNAL
       BCACHE_wb((Ptr)hDescriptor, sizeof(hDescriptor), 0 /* no wait */);
    #endif

    I don't expect the changes to take very long to implement if you wish to get things working in external memory.  FYI, the BCACHE functions I used above are part of DSP/BIOS so just #include <bcache.h> to use them!

    Best regards,
    Brad

  • Hi Brad,

    Thank you for the example code. It is very helpful to have some code to start with.

    I appreciate that I need to align descriptors and keep the rest of the 128 bytes unoccupied. I have no problem with this and I anticipate that with some experimenting I can get the cache writeback/invalidate to work as well as far as descriptors or other structures that my USB driver manages are concerned.

    However, besides the descriptors, there are actual data buffers. That is, the transfer descriptors point to places in (external or internal) memory and the OHCI controller then transfers these over the USB. For bulk transfers, each transfer descriptor can have attached to it a buffer of up to 64bytes (as a maximum, the endpoint MaxPacketSize may be smaller). However, I am anticipating that the interface to my UsbDriver will want to request larger transfers, say 4Kb (=64*64). Now if buffers need to be aligned for the OHCI to work, my driver has to split, align and pad the 4Kb buffer (which iscontiguous in memory) into 64 buffers of 64 byte buffers occupying one cache line each.

    If you have any suggestions on how to do this efficiently this would be very helpful.

    Kind regards,

    Mark

     

  • Hi again,

    After the message I just posted I am becoming a bit unsure as to whether I got the right end of the stick and I indeed need to split these buffers as far as OHCI is concerned. It depends on ones interpretation of section 5.2.8.2. of the OHCI specification and, in particular, the example code in this section that deals with the queueing TDs for data packets.

    So, another question is, with OHCI, is it allowed to queue a TD for a (bulk) endpoint that has a buffer that is larger than the maximum packet size of the endpoint? I suppose if the answer is yes that would explain a few things!

    Kind regards,

    Mark

  • Mark K said:
    However, I am anticipating that the interface to my UsbDriver will want to request larger transfers, say 4Kb (=64*64). Now if buffers need to be aligned for the OHCI to work, my driver has to split, align and pad the 4Kb buffer (which iscontiguous in memory) into 64 buffers of 64 byte buffers occupying one cache line each.

    If the application is trying to send a 4KB buffer then I don't see any need to split, align, and pad.  The only requirement is for the buffer itself (i.e. the 4KB) to be aligned on a 128 byte boundary and that it occupy a multiple of 128 bytes.  So if for some reason you were trying to send 4097 bytes you would need to pad the buffer to 4224 bytes.

    So if we stick to the example of sending 4096 bytes you would need to setup 64 descriptors to transfer 64 bytes each.  So you would setup descriptor0 to point to the start of the buffer, descriptor1 to point to byte64, descriptor2 to point to byte128, etc.  Before kicking off the transfer you would perform a BCACHE_wb to the buffer to force all the data out to the physical external memory.  As long as their is well defined ownership it will not be necessary to segment the buffers.  In other words, you can setup the descriptors as I have described above, writeback your data,  and then kick off your USB transfer under the condition that only the USB will be touching the 4KB buffer.

    If on the other hand you were receiving  a 4KB buffer you would setup your descriptors, kick off your USB transfer, invalidate the buffer with BCACHE_inv, and then the CPU could read from the 4KB buffer.  Again the only restriction is that the CPU and USB cannot access the buffers simultaneously.

  • Hi Brad,

    Thanks for that, it seems I am good at making invalid assumptions!

    I think I now have all the information I need to try and get my driver to work with external memory. I'll try that now.

    Kind regards,

    Mark