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.

TMS320C6748: Communication between the DSP and a NOR flash memory by the EMIFA

Part Number: TMS320C6748
Other Parts Discussed in Thread: OMAP-L138

Hi,

I'm trying to read from and write to a NOR flash memory using the EMIFA protocol.

I'm able to configure the registers of the EMIFA related to the NOR memory, but i don't know how to the the DSP to read or write because i asume that is not by pulling up and down the CS,  OW, etc, explicitly, it may be a function or something that starst the process or comunication, but i can't find it.

Could you help me? or do you know where could i find a code example of a NOR communication by EMIFA? because i didn't find nothing about it.

Thanks,

Juan

  • Hi Juan,

    I've notified the sw team. They will post their feedback directly here.

    Please share which RTOS SDK version are you using.

    Best Regards,
    Yordan
  • Juan,

    I will refer you to the EMIFA User Guide or the EMIFA section of the TRM for the C6748. Of available TI documentation that I know of, that EMIFA section of the EMIFA UG or TRM is the best place to see how the pins work. The documentation only assumes a basic understanding of the behavior of a microprocessor memory bus, so it should add some clarification to how the pins are used. There is also a section with examples of how to configure the EMIFA and what the waveforms will look like.

    Regards,
    RandyP
  • Not sure what you are asking. Once you set up the one of the CEnCFG register, the NOR is a memory mapped device, eg:

    CE2CFG 0x60000000 Base Address
    CE3CFG 0x62000000 ...
    CE4CFG 0x64000000 ...
    CE5CFG 0x66000000 ...

    Reading from that NOR means you read from the memory address range associated with that CS. Writing to NOR depends on the manufacturer of the NOR chip. Broadly speaking, the world in divided into Intel 2-byte and AMD 3-byte command sequences. TI has some example code called NORWriter buried in many of their packages. I believe the online respository is at:
    sourceforge.net/.../
    All open source projects like u-boot and linux have code to write to NOR.
  • Hi Norman, RandyP,

    What i am asking is what do i have to do after setting up the CEnCFG register (with the strobe time and those things) to communicate with an external NOR.

    In the case of the I2C i had to set a register that enables the master mode, then another register that sends a START bit (and some other staff) and then wait till a register that indicates when a data is received gets set so i could read that data from another register (a similar process for sending data).

    So the questions is how do i do the same process to communicate with the NOR? is there a register that when set the EMIF pulls down the CS pin,  waits the set up time, pull down  OE, waits the strobe time, etc. and then stores the data in any register?

    Because i asume that if you have to specify those times is because the DSP does that process for you, isn't it?

    I know my question is a little bit confusing...

    Thanks,

    Juan

  • Hi Norman,

    I find that you answered a question similar to mine here:

    showing this code simple:

    void fpga_init(void)
    {
      PSCModuleControl(SOC_PSC_0_REGS,
                       HW_PSC_EMIFA,
                       PSC_POWERDOMAIN_ALWAYS_ON,
    
                       PSC_MDCTL_NEXT_ENABLE);    
    
    
      HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5) =
      (HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5)&
                                ~SYSCFG_PINMUX5_PINMUX5_31_28)|
                                (SYSCFG_PINMUX5_PINMUX5_31_28_EMA_BA0<<
                                 SYSCFG_PINMUX5_PINMUX5_31_28);
      /* Repeat for EMA_A0-EMA_A? and EMA_D0-EMA_D15, any other EMA signals */
      
      /* Miniimally set the CFG register. Set to maximum times if unsure. */
      HWREG(SOC_EMIFA_0_REGS+EMIFA_CE3CFG)
        = 0x00 << 31 /* 0-1  SS */
        | 0x00 << 30 /* 0-1  EW */
        | 0x0F << 26 /* 0-F  W_SETUP */
        | 0x3F << 20 /* 0-3F W_STROBE */
        | 0x07 << 17 /* 0-7  W_HOLD */
        | 0x0F << 13 /* 0-F  R_SETUP */
        | 0x3F <<  7 /* 0-3F R_STROBE */
        | 0x07 <<  4 /* 0-7  R_HOLD */
        | 0x03 <<  2 /* 0-3  TA */
        | 0x01 <<  1;/* 0-3  ASIZE, 0=8-bit,1=16-bit */
    
      /* Now can access at CSn_ADDR */
    }
    
    void fpga_set(int i, uint16 v)
    {
      volatile uint16 *p = (volatile uint16 *)SOC_EMIFA_CS3_ADDR;
      p[i] = v;
    }
    
    uint16 fpga_get(int i)
    {
      volatile uint16 *p = (volatile uint16 *)SOC_EMIFA_CS3_ADDR;
      return(p[i]);
    }

    What i don't understand is... if i want to read from the 0004h memory address of my NOR... how do a put the 0004h address in the EMA_A[x:0]?

    it could be something like:

    uint16 fpga_get(int i)
    {
      volatile uint16 *p = (volatile uint16 *)SOC_EMIFA_CS3_ADDR + 04h;
      return(p[i]);
    }

    or 

    void fpga_set(int i, uint16 v)
    {
      volatile uint16 *p = (volatile uint16 *)SOC_EMIFA_CS3_ADDR +04h;
      p[i] = v;
    }

    ???

    Thanks for your time,

    Juan

  • I think you might be overthinking it a bit. It just normal address arithmetic. You treat your device as an array of 8-bit or 16-bit values. The only weirdness is is that EMA_BA[1:0] are always the bottom 2 bits in an address. All that other pins like CS and WS are automatically handled when you access that "array".

    In the code snippet you posted, the offset is passed in. So to access the cell #4 of 16-bit wide NOR device, you would pass in 4 for the parameter, eg:

    v = fpga_get(4);
    fpga_set(4, v);

    The actual address for is 4 x 2 = 8 or binary 00001000. On the address pins:

    EMA_A[5] = 0
    EMA_A[4] = 0
    EMA_A[3] = 0
    EMA_A[2] = 0
    EMA_A[1] = 1
    EMA_A[0] = 0
    EMA_BA[1] = 0
    EMA_BA[0] = not defined for 16-bit


    EDIT: Correct code.

  • Aaaah, I didn’t realized there was index to access to a particular address.

    In that case is simpler than what i was thinking.

    So if i am not wrong... for 16-bit you pass the address you want to that function, for 8-bits is 2xaddress because is shifted due to the EMA_BA1.

    Another three questions..

    Is the same for SDRAM?

    For 16-bits Is the EMA_BA0 or BA1?, because the company who provides us the hardware sends a document where the one used is the EMA_BA0 not the BA1 as you said.

    My NOR’s data *** doesn’t  show strobe, hold, etc. Times.

    So... Which values do you recomend?

    Thaks Norman, I’m gonna mark your last reply as the correct answer.

    Juan

  • The access address always maps to a specific pin regardless of bus width. So lets say your address in code is
    A7,...,A2,A1,A0
    the associated pins are always
    EMA_A[5],...,EMA_A[0],EMA_BA[1],EMA_BA[0]

    On an 8-bit device, EMA_BA[0] is used and access would be on every address, eg, 0,1,2...

    On a 16-bit device, EMA_BA[0] is not used and access would be on even addresses, eg. 0,2,4,... If you declare your array as 16-bit integers, then the index would 0,1,2... Accessing the 16-bit device on odd addresses or 8-bits at a time depends on the cpu and the device. I would not recommend it.

    It your NOR datasheet does not have the timing values, you can always set it to the maximum slowest values in the CEnCFG register. I am a firmware guy, I usually have a hardware guy to spec out the timing. You can try starting another thread with your NOR part and there are HW guys out there that will probably suggest reasonable timing values.

    SDRAM is quite a bit different in HW connection. See the TRM for details.

  • Ok, i'll ask later about the NOR timing values.

    I'm a bit confused about the address with 8/16 -bit, could you tell me which would be the value of EMA_A[5],...,EMA_A[0],EMA_BA[1],EMA_BA[0] assuming an 8-bits address A7,...,A2,A1,A0 on this cases?

    16-bit data bus:

    v = fpga_get(4);

    v = fpga_get(5);

    8-bit data bus:

    v = fpga_get(4);

    v = fpga_get(5);

    Thanks for the help Norman,

    Juan

  • First off. You would probably never read from NOR memory using a function. You would just read from it as though it an array. Writing would require a rather complex function. You cannot write to it though it was an array. See the example NORWriter code. For purposes of discussion,

    volatile uint8_t  *base8 = (volatile uint8_t *)SOC_EMIFA_CS3_ADDR;
    volatile uint16_t *base16 = (volatile uint16_t *)SOC_EMIFA_CS3_ADDR;
    
    void    emif8_set(int i, uint8_t v) {  base8[i] = v;     }
    uint8_t emif8_get(int i)            {  return(base8[i]); }
    
    void     emif16_set(int i, uint16_t v) {  base16[i] = v;     }
    uint16_t emif16_get(int i)             {  return(base16[i]); }
    

    emif8_get(4) -> addr=4x1 -> A7:A0 = 00000100
    emif8_get(5) -> addr=5x1 -> A7:A0 = 00000101

    emif16_get(4) -> addr=4x2=8 -> A7:A0 = 00001000
    emif16_get(5) -> addr=5x2=10 -> A7:A0 = 00001010

    The mapping between internal address in code to external pins is always the same:


    EMA_A[5] = A7
    EMA_A[4] = A6
    EMA_A[3] = A5
    EMA_A[2] = A4
    EMA_A[1] = A3
    EMA_A[0] = A2
    EMA_BA[1] = A1
    EMA_BA[0] = A0

  • Ok, now i got what you mean about the addresses.

    Talking about the read and write action...

    Why it is not common to use a function to read?

    i was thinking in something like this:

    READ

    void     emif16_set(int i, uint16_t v) {  base16[i] = v;     }

    WRITE

    uint16_t emif16_get(int i)             
    {
        //send command byte (maybe calling a function designed to do that)
    
        return(base16[i]); 
    }

    As i see it is like accesing as an array but stored in a function, isn't it?

    Thanks,

    Juan

  • The example get/set code makes sense for a few random access FPGA registers. It does not make that much sense for NOR memory because it is much larger. The get/set functions can really slow down performance for a large number of accesses. A typical application of NOR memory is for actual code. The processors directly fetche-executes from NOR memory. No read function allowed. Other memories like NAND need to be copied to RAM for execution. Writing NOR memory require at least 4 accesses for each word written. Having to call a function to access the bus would slow it down several times more. You could inline the get/set functions to try to reduce the overhead.

    void nor_write_word(int i, uint16_t v)
    {
      base16[0x555] = 0xAA; // 0xAAA on address lines
      base16[0x2AA] = 0x55; // 0x555 on address lines
      base16[0x555] = 0xA0; // Program command, 0xAAA on address lines
      base16[i]     = v;    // Address and data
    
      // More code to poll for completion, etc.
      ...
    }
    

  • That’s what i was thinking,
    so that means that you wsut between transferences?
    It stores the four data in a buffer or something like that until the previous data reach the memory?
    And how could be the polling for completion code? Because there is not a register or something that indicates the end of the transference.

    And in the case that you want a faster way to write/read, maybe for a large amount of data, how should be done instead of using the above function?

    Thank you for your time,
    Juan
  • On your NOR device, to program one word you have to write 2 setup/unlock words, 1 command word and one actual data word. Flash memory contains a state machine that move from state to state on each word written. This is to avoid accidentally programming the flash.

    Programming or erasing flash memory takes time. During that time. reads don't work as expected. For most flash memory. the read is not data but a status. A 5th word is required to return the flash back to read mode.

    For the time being, ignore my comments about performance. Key is to understand NOR flash first.

    Look at how others have done it to see how to send commands to the flash memory.

    TI's flash utiliy here:
    sourceforge.net/.../
    OMAP-L138_FlashAndBootUtils_2_40.tar.gz
    OMAP-L138_FlashAndBootUtils_2_40/Common/drivers/src/nor.c:AMD_Write()

    U-boot code:
    git.denx.de/
    drivers/mtd/cfi_flash.c:flash_status_poll()
    drivers/mtd/cfi_flash.c:flash_write_cfiword()

    Linux code:
    git.kernel.org/.../cfi_cmdset_0002.c
    drivers/mtd/chips/cfi_cmdset_0002.c:do_write_oneword()
  • that helps a lot.

    i'll be working on that and i would ask again if i have questions.

    Thanks Norman.

    Juan
  • Please start a new threads for new questions. You'll probably be forced to anyways as idle threads appear to get locked now.
  • ok, for comming questions i ouldopen a new thread.

    The last thing about what you suggested me yesterday...

    Could you explain me the yelow lines?

    I don't understand them stand for.

    static VUint8 *LOCAL_flashMakeAddr (NOR_InfoHandle hNorInfo, Uint32 blkAddr, Uint32 offset)
    {
      return ((VUint8 *) ( blkAddr + (offset * hNorInfo->maxTotalWidth)));
    }

    static void LOCAL_flashMakeCmd (NOR_InfoHandle hNorInfo, Uint8 cmd, void *cmdbuf)
    {
      Int32 i;
      Uint8 *cp = (Uint8 *) cmdbuf;

      for (i = hNorInfo->busWidth; i > 0; i--)
        *cp++ = (i & (hNorInfo->chipOperatingWidth - 1)) ? 0x00 : cmd;
    }

    Not the code, but what it is used for

    Thanks,

    Juan

  • My guess is that code is intended to handle different widths of bus and different widths of chips in a generic way.

    The LOCAL_flashMakeAddr() is doing the same calculation as using the compiler to calculate the address of an array element:
    &blkAddr[offset]
    The compiler requires blkAddr to be declared with a specific type (Uint8 or Uint16) to do the calc. The function does the address calculation at runtime.

    The function LOCAL_flashMakeCmd() appears to put the cmd byte on each device across the data bus. Most of the time, there is only one device and you would just write one cmd byte to all 8 or 16 data bits. In the case where two 8-bit devices are attached to 16-bit bus, ie device 1 is on A[7:0], device 2 is on A[15:8]; then you want to write the cmd byte to both upper and lower bytes of the data bus.

    If you are writing your NOR driver, you can simplify everything to handle just your specific situation. Easier to understand. The only drawback is that you would have to rewrite everything to handle a HW change.

    A side note. Please respond to your other thread regarding the XDS100.
  • Hi Norman,

    Yes, i'm changing the code to work only with my Nor, but i'm a bit confused of what are the elements of the hNorInfo structure and what means the different values for each one, i have to figure it out so i can't modify the code properly.

    I'll have in mind your answer of above and if you don't mind i'll ask again if i have a problema.

    PD: XDS100 thread answered.

    Thanks,
    Juan
  • Hi Norman,

    I'm really confused with the code in  yellow:

    /////////////////////////////////////////////////////////////////////////

    // AMD Flash Write

    static Uint32 AMD_Write(NOR_InfoHandle hNorInfo, Uint32 address, VUint32 data )

    {

     Uint32 retval = E_PASS;

     // Send Commands

     LOCAL_AMDPrefixCommands(hNorInfo);

     LOCAL_flashWriteCmd(hNorInfo,hNorInfo->flashBase, AMD_CMD2_ADDR, AMD_PROG_CMD);

     LOCAL_flashWriteData(hNorInfo,address, data);

     // Wait for ready.

     while(TRUE)

     {

       if ( (LOCAL_flashReadData(hNorInfo, address, 0 ) & (BIT7 | BIT15) ) == (data & (BIT7 | BIT15) ) )

       {

         break;

       }

       else

       {

         if(LOCAL_flashIsSetAll(hNorInfo, address, 0, BIT5))

         {

           if ( (LOCAL_flashReadData(hNorInfo, address, 0 ) & (BIT7 | BIT15) ) != (data & (BIT7 | BIT15) ) )

           {

             DEBUG_printString("Timeout ocurred.\r\n");

             retval = E_FAIL;

           }

           break;

         }

       }

     }

     // Return Read Mode

    AMD_SoftReset(hNorInfo);

    // Verify the data.

     if ( (retval == E_PASS) && ( LOCAL_flashReadData(hNorInfo, address, 0) != data) )

       retval = E_FAIL;

    return retval;

    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////

    What i don't understand is why it is comparing the 7th or 15th bit of the address where it writes with the 7th or 15th bit of the data to be written in order to check if it's ready and what does the flashIsSetAll() do, and why it uses the 5th bit or 1st bit to check if a timeout or an abort occurs like does in the following code:

    //////////////////////////////////////////////////////////////////////////////////////

    // AMD flash buffered write
    static Uint32 AMD_BufferWrite(NOR_InfoHandle hNorInfo, Uint32 address, VUint8 data[], Uint32 numBytes )
    {
      Uint32 startAddress = address;
      Uint32 blkAddress, blkSize;
      Uint32 data_temp;
      Uint32 retval = E_PASS;
      Uint32 shift;
      volatile NOR_Ptr pAddr, pData;
      VUint8* endAddress;
     
      // Get block base address and size
      NOR_getBlockInfo(hNorInfo, address, &blkSize, &blkAddress);
       
      // Write the Write Buffer Load command
      LOCAL_AMDPrefixCommands(hNorInfo);
      LOCAL_flashWriteCmd(hNorInfo, blkAddress, 0, AMD_WRT_BUF_LOAD_CMD);
           
      //Establish correct shift value
      shift = 0;
      while ((hNorInfo->busWidth >> shift) > 1)
        shift++;
       
      // Write Length (either numBytes or numBytes/2)    
      LOCAL_flashWriteCmd(hNorInfo, blkAddress, 0, (numBytes >> shift) - 1);
     
     // Write Data
      pData.cp = (VUint8*) data;
      pAddr.cp = (VUint8*) address;
      endAddress =(VUint8*)(pAddr.cp + numBytes);
      while (pAddr.cp < endAddress)
      {
        switch (hNorInfo->busWidth)
        {
          case BUS_8BIT:
            *pAddr.cp++ = *pData.cp++;
            break;
          case BUS_16BIT:
            *pAddr.wp++ = *pData.wp++;
            break;
        }
      }

      // Put last data written at start of data buffer - For AMD verification
      switch (hNorInfo->busWidth)
      {
        case BUS_8BIT:
          address = (Uint32)(endAddress-1);
          break;
        case BUS_16BIT:
          address = (Uint32)(endAddress-2);
          break;
      } 
     
      
      // Program Buffer to Flash Confirm Write
      LOCAL_flashWriteCmd(hNorInfo, blkAddress, 0, AMD_WRT_BUF_CONF_CMD);                 

      UTIL_waitLoop(1000);
       
      // Read last data item                 
      data_temp = LOCAL_flashReadData(hNorInfo, (Uint32) (data + (address - startAddress)), 0);
           
     while(TRUE)
     {
        if( (LOCAL_flashReadData(hNorInfo, address, 0 ) & (BIT7 | BIT15)) == (data_temp & (BIT7 | BIT15) ) )
        {
          break;
        }
        else
        {
          // Timeout has occurred
          if(LOCAL_flashIsSetAll(hNorInfo,address, 0, BIT5))
          {
            if( (LOCAL_flashReadData(hNorInfo,address, 0 ) & (BIT7 | BIT15)) != (data_temp & (BIT7 | BIT15) ) )
            {
              DEBUG_printString("Timeout ocurred.\r\n");
              retval = E_FAIL;
            }
            break;
          }
          // Abort has occurred
          if(LOCAL_flashIsSetAll(hNorInfo, address, 0, BIT1))
          {
            if( (LOCAL_flashReadData(hNorInfo, address, 0 ) & (BIT7 | BIT15)) != (data_temp & (BIT7 | BIT15) ) )
            {
              DEBUG_printString("Abort ocurred.\r\n");
              retval = E_FAIL;
              LOCAL_AMDWriteBufAbortReset (hNorInfo);
            }
            break;
          }
        }
      }
     
      // Put chip back into read array mode.
      AMD_SoftReset(hNorInfo);
      if (retval == E_PASS)
      {
        retval = LOCAL_flashVerifyDataBuffer(hNorInfo, startAddress,(void*)data, numBytes);
        if (retval != E_PASS)
          DEBUG_printString("Data verify failed.\r\n");
      }
      return retval;
    }

    // AMD Write Buf Abort Reset Flash
    static void LOCAL_AMDWriteBufAbortReset(NOR_InfoHandle hNorInfo)
    {
      // Reset Flash to be in Read Array Mode
      LOCAL_AMDPrefixCommands(hNorInfo);
      AMD_SoftReset(hNorInfo);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////

    I hope you could help me.

    Thanks,

    Juan

  • See the datasheet for your device S29AL016J. Section
    11. Write Operation Status
    The code supports a 8-bit chip on the upper byte of the data bus so it also checks BIT15. In your case with only one chip, you would check BIT7 or DQ7 in the datasheet.
  • Ok, i got it.
    Thanks,
    Juan