• 0

CMOD A7 with Micron flash programs and runs fine; programs but does not run on board with Macronix flash.


Question

We've been using the CMOD A7s for some time without issue. Recently, we received a batch of boards with Macronix flash chips instead of the Micron chips and while they seem to program fine from the SDK, they don't seem to run the firmware after reset. Unfortunately, our FPGA developer is no longer here, and I have extremely limited experience with this stuff. I'm an MCU guy, not an FPGA guy.

Other than selecting the correct memory device when programming the flash from the SDK, is there anything else I need to change for these new boards with the Macronix flash?

Link to post
Share on other sites

15 answers to this question

Recommended Posts

  • 0
1 hour ago, Kyle_Jackson said:

I feel like the solution must be something simple I'm just missing.

Perhaps you should be feeling like the solution is your board vendor's responsibility. Any product production change that changes the behavior of a product is not the responsibility of the customer. Any time a component is replaced there is a chance of this happening. Unfortunately, sometimes due to component availability this is a necessity.  What should happen is that someone should be testing board revisions thoroughly. Some vendors are better at allocating resources to doing proper testing than others. When you don't create qualification demos that validate all of the product functionality you're likely to miss important details. Lets, face it. This is a low margin product. But that's not important to a customer wanting to have a product available in quantities over a long time frame.

Try shooting a few more, perhaps better aimed arrows. But, it's good to have this issue prominently displayed for other customers and potential customers.

Likely, this is an unfortunate time of the year to be running into this so there might not be a lot of people seeing the smoke signals.

Edited by zygot
Link to post
Share on other sites
  • 0

Thanks, zygot. It is frustrating that Digilent called the Macronix flash a "drop in replacement" for the Micron in their reference docs when it's apparently not.

 

Thanks, Ana-Maria.

I've made some progress on figuring out what's going on, but no progress towards a solution.

Debugging from the SDK the SPI bootloader seems to crash when it tries to access the flash.

This project was created in Vivado 2017.2. In the SDK source there's no device information for Macronix flash, nor is it an option in the QSPI block customization in Vivado. I opened the project in Vivado 2020.2, and there is now the option to select Macronix flash in the QSPI block customization as the slave device. However, enough stuff has changed between these versions that I can't move forward due to other errors and due to my inexperience I have no idea how to fix them.

After some further searching I've discovered Micron has obsoleted the flash used in these boards, hence the switch to Macronix. It's really unfortunate that these CMOD boards have the same part number but require a change to the original design to work.

Alliance Memory seems to have purchased to rights to manufacture these legacy Micron parts, so I'm going to try replacing the Macronix flash with this Micron-like part and see if that works. Hopefully device and manufacturer IDs remain the same and it Just Works. If that fails I suppose my next step is hire a contractor to get our project running in a newer version of Vivado. Or, perhaps just re-implement this fairly simple project in an MCU.

Link to post
Share on other sites
  • 0
17 minutes ago, Kyle_Jackson said:

It is frustrating that Digilent called the Macronix flash a "drop in replacement" for the Micron in their reference docs when it's apparently not.

Adding alternate vendor part number to a BOM in order to do a production run is always prone to error. Complex parts deserve extra attention to detail as component manufacturers tend to overstate compatibility with competitor parts. Using a new part for a product run and sending the results into distribution without extensive functional testing is not a mistake that should ever be made, but this happens. This especially happens when functionality is not a supported feature of the product, which in not uncommon for FPGA development hardware.

At least you have an indication of movement on your issue. Hoping that you are back to doing what you'd want to be doing in short order.

Link to post
Share on other sites
  • 0

I have been looking into both the Macronix and Micron flash chips over the last few days to understand their similarities and differences, and I have a pretty good idea now about which functions work the same, and which work differently between the two chips.

For what it's worth, these are my findings. I hope it may be of use.

The following commands work the same:

  • READ ( 03h), no dummy cycles between address and data.
  • DREAD (3Bh), needs 1 dummy write (8 cycles) between address and data.
  • FREAD a.k.a. FAST READ (0Bh), needs 1 dummy write (8 cycles) between address and data.
  • QREAD (6Bh), needs 1 dummy write (8 cycles) between address and data. HOWEVER, on the Macronix chip, quad-bit data mode needs to be explicitly enabled; on the Micron chip, it is unconditionally available.
  • PP a.k.a. PAGE PROGRAM (02h)
  • SE a.k.a. 4K SECTOR ERASE (20h)
  • WREN (06h) sets the "write enable" bit in the status register. This command needs to precede the PP and SE commands.

The following commands work differently:

  • 2READ( BBh), the Macronix needs 1 dummy write (4 cycles, 2 bits/cycle), the Micron needs 2 writes (8 cycles, 2 bits/cycle).
  • 4READ (EBh), the Macronix needs 3 dummy writes (6 cycles, 4 bits/cycle), the Micron needs 5 (10 cycles, 4 bits/cycle).

The Micron chip supports "Dual" and "Quad" command modes, where even the initial command byte can be sent using multiple DQ bits (2 or 4, respectively). On the Macronix chip, the initial command bytes is always sent over 1 bit only (DQ0 aka MOSI).

The configuration and status registers are also quite different between the 2 chips, but fortunately the RDSR command (05h) works on both, and even though the status registers are different, the most important bits "WIP" (bit 0) that is used to monitor "write in progress" of the PP and SE command work identically.

Myself, I have decided to just work with a minimal subset of the Flash commands that is identical between the two chips and not subject to chip-dependent configuration. In particular, the commands "READ", "PAGE PROGRAM", and "4K SECTOR ERASE", "WREN" and "RDSR" (the latter to monitor the write-in-progress bit) do everything I need and require minimal code and effort to get working.

The read performance is a factor 4 lower than it could be (using the 4READ or QREAD commands), but for my application that isn't relevant and I prefer not having to do chip-dependent stuff.

 

Edited by reddish
Link to post
Share on other sites
  • 0

Thanks for that information, reddish. We're lucky to have someone very skilled at PCB rework, thus it's fairly trivial for us to replace the Macronix flash IC with the Alliance Memory Micron clones. At our relatively low volumes, the additional cost and time is negligible. I can confirm that the Alliance ICs look just like the Micron ICs to the Xilinx SDK and Vivado, and they flash and run just fine.

Digilent, it might be worth looking into these Alliance Memory ICs, which truly are a "drop-in replacement" for the Micron ICs, for future product runs.

Link to post
Share on other sites
  • 0

To Zygot's point R&D did test Macronix flash with end applications and was aware that the Macronix part is not a drop in replacement. Programming the part from within the Xilinx tools requires different steps (selecting a different IC) and accessing it from an application using Xilinx's QSPI core requires different and/or extra steps. That's why a flash memory rarely gets swapped out on a Digilent product, even when procurement or marketing come to R&D wanting to replace a part for cost reduction and/or joint marketing opportunities. We try to avoid switching out flash unless there's no other option. In this particular case procurement notified R&D that the original Micron flash was EOL and that soon it would no longer be possible to purchase that component. At that time the Macronix part was the only one identified that would fit within the physical space constraints of the Cmod A7 and thus we chose to use that part.

I just looked through the manufacturing test and see that it was updated to support multiple flash memory options, including Macronix, on September 8 of 2017. So there has been quite a bit of delay between when procurement asked R&D to replace the flash and when customers started receiving modules with the Macronix flash. The fact that this forum thread exists means that we failed to properly document the changes required to use the Macronix flash, and I'm sorry for any hardship that has caused anyone.

The following code snippet was adapted from an Xilinx example project for their QSPI core and should work with Micron, Spansion, and Macronix flash. As far as I can remember the main difference between communicating with Micron and Macronix flash is related to the number of dummy cycles required when performing dual and quad read operations. Spansion and Macronix flash use the same dummy count, while Spansion flash also requires an extra step (sending a command to explicitly enable quad mode). Please note that cbDualReadDummy, cbQuadReadDummy, cbDualIOReadDummy, cbDualIOReadDummy, and cbQuadIOReadDummy are global variables used by the read function when performing a read operation of a specific type.

#define cmdReadID 0x9F /* Read manufacturer ID of SPI Flash */

#define cbDualReadDummyMicron        2
#define cbQuadReadDummyMicron        4
#define cbDualIOReadDummyMicron        2
#define cbQuadIOReadDummyMicron        5

#define cbDualReadDummySpansion        4
#define cbDualIOReadDummySpansion    2
#define cbQuadReadDummySpansion        4
#define cbQuadIOReadDummySpansion    3

        /* Read the manufacturer ID from the SPI Flash.
    */
    if ( ! FSpiFlashGetManufactureID(pxspiFlash) ) {
        if (fVerbose ) {
            xil_printf("Error: failed to read manufacturer ID!\r\n");
        }
        return fFalse;
    }

    if ( 0x01 == rgbRead[1] ) {
        if (fVerbose ) {
            xil_printf("Spansion Flash!\r\n");
        }
        fSpansionFlash = fTrue;
        cbDualReadDummy = cbDualReadDummySpansion;
        cbQuadReadDummy    = cbQuadReadDummySpansion;
        cbDualIOReadDummy = cbDualIOReadDummySpansion;
        cbQuadIOReadDummy = cbQuadIOReadDummySpansion;
    }
    else if ( 0x20 == rgbRead[1] ) {
        if (fVerbose ) {
            xil_printf("Micron Flash!\r\n");
        }
        fSpansionFlash = fFalse;
        cbDualReadDummy = cbDualReadDummyMicron;
        cbQuadReadDummy    = cbQuadReadDummyMicron;
        cbDualIOReadDummy = cbDualIOReadDummyMicron;
        cbQuadIOReadDummy = cbQuadIOReadDummyMicron;
    }
    else if ( 0xC2 == rgbRead[1] ) {
        if (fVerbose ) {
            xil_printf("Macronix Flash!\r\n");
        }
        fSpansionFlash = fFalse;
        cbDualReadDummy = cbDualReadDummySpansion;
        cbQuadReadDummy    = cbQuadReadDummySpansion;
        cbDualIOReadDummy = cbDualIOReadDummySpansion;
        cbQuadIOReadDummy = cbQuadIOReadDummySpansion;
    }
    else {
        if (fVerbose ) {
            xil_printf("Unknown Flash!\r\n");
        }
        fSpansionFlash = fFalse;
        return fFalse;
    }

BOOL FSpiFlashGetManufactureID(XSpi* pxspiFlash) {

    /* Wait until the flash is ready to accept the next command.
    */
    if ( ! FSpiFlashWaitForFlashReady(pxspiFlash) ) {
        return fFalse;
    }

    rgbWrite[0] = cmdReadID;
    fTransferring = fTrue;
    if ( XST_SUCCESS != XSpi_Transfer(pxspiFlash, rgbWrite, rgbRead, cbStsRead) ) {
        return fFalse;
    }

    /* Wait for the transfer to complete and then check for errors.
    */
    while ( fTransferring ) {
        asm("nop");
    }

    if( 0 != cerrTrans ) {
        cerrTrans = 0;
        return fFalse;
    }

    return fTrue;
}

BOOL FSpiFlashRead(XSpi* pxspiFlash, u32 addrRead, u32 cbRead, u8 cmdRead) {

    /* Wait until the flash is ready to accept the next command.
    */
    if ( ! FSpiFlashWaitForFlashReady(pxspiFlash) ) {
        return fFalse;
    }

    /* Populate the buffer with the read command and starting address.
    */
    rgbWrite[0] = cmdRead;
    rgbWrite[1] = (u8) (addrRead >> 16);
    rgbWrite[2] = (u8) (addrRead >> 8);
    rgbWrite[3] = (u8) addrRead;
    rgbWrite[4] = 0;

    /* Certain read commands require additional bytes to be transferred.
    ** Extend the read byte count if required for the specified command.
    */
    if ( cmdDualRead == cmdRead ) {
        cbRead += cbDualReadDummy;
    }
    else if ( cmdDualIORead == cmdRead ) {
        cbRead += cbDualIOReadDummy;
    }
    else if ( cmdQuadIORead == cmdRead ) {
        cbRead += cbQuadIOReadDummy;
    }
    else if ( cmdQuadRead == cmdRead ) {
        cbRead += cbQuadReadDummy;
    }

    /* Transfer the write command and the data to the flash and perform
    ** the requested read.
    */
    fTransferring = fTrue;
    if ( XST_SUCCESS != XSpi_Transfer(pxspiFlash, rgbWrite, rgbRead, (cbRead + cbRWExtra)) ) {
        return fFalse;
    }

    /* Wait for the transfer to complete and then check for errors.
    */
    while ( fTransferring ) {
        asm("nop");
    }

    if( 0 != cerrTrans ) {
        cerrTrans = 0;
        return fFalse;
    }

    return fTrue;
}

I hope these code snippets are helpful for anyone having issues reading from the Macronix flash.

@Kyle_Jackson I don't think we will change to the Alliance Memory part because doing so would likely cause more issues, with a mix of Macronix boards already in the field. However, I would appreciate it if you can provide the part number for the IC that used so that I can note it just in case we do decide to make a change.

Thanks,
Michael

Link to post
Share on other sites
  • 0

@malexander The part number is exactly the same, N25Q032A13EF440F. I do understand changing again would just introduce a whole new layer of potential issues. I do recommend making some changes to the reference manual, though, as it currently states:

Quote

In manufacturing, parts sometimes need to be replaced when the product goes end-of-life. The Quad SPI Flash memory on any particular Cmod A7 may be one of the drop-in replacements found in the Table 4.1 below.

I know now that "drop-in" applies to the footprint of the device package only, whereas I obviously took it to mean something different. Nowhere does it state that the different parts require source code or design changes on the part of the user. That would have been a helpful note to save the inexperienced (like me) much troubleshooting time.

Thanks also for the code snippet. I'll see if I can get that working when I have a chance. For the moment, we've simply replaced the flash ICs on enough modules to carry us for a while.

Link to post
Share on other sites
  • 0
3 hours ago, Kyle_Jackson said:

I know now that "drop-in" applies to the footprint of the device package only

No, I'd disagree with that comment. Drop-in means that no PCB or board design revisions are required to use any qualified alternate part on the BOM. This certainly includes the footprint. It also means that a customer who might be trying to use multiple pieces of a product that aren't from the same manufacturing run won't notice a change in components. This is an important point from the customer's viewpoint as it can make their life a lot more complicated.

Sometimes, there are no suitable alternate parts available to do a production run. The customer should never be the one to get the surprise. It's the vendor's responsibility to let customers know in advance it they start shipping product that isn't completely compatible in any way with previous copies of a product. If someone wants to OEM a module like the CMOD then they deserve to be allowed to decide whether or not it makes sense to have the burden of maintaining code for multiple versions of a component that isn't even identifiable as unique from other versions. 

Even if Digilent did the right thing and made it clear on the sales and product pages that certain versions of a product had unique labels like a run # or date codes signifying different behaviors, and, in the case of something like FLASH, they provided IP that detected the FLASH in the product and handled the unique aspects of every version of the product that was ever produced they have an obligation to make this known in advance of a sale. Yes, for most CMOD customers this kind of thing isn't a big deal. If you are going to OEM a product to other companies then you have a responsibility to consider their needs.

Edited by zygot
Link to post
Share on other sites
  • 0

@Kyle_Jackson

I was able to get the SREC bootloader working on a Cmod A7 - 35T that has the Macronix flash using Vivado 2019.1. To get this to work you have to include the Xilinx QSPI controller in your Microblaze block design and in the IP configuration either select "Standard" for the mode (SPI x1 mode) or if you select "QUAD" then make sure to select "Macronix" for Slave Device.

Once you are in SDK and have created the SREC bootloader project (and associated BSP) you need to open "bootloader.c" from the sources and scroll down until you find the call to "XIsf_Initialize". Double click the function name to highlight it, right click and select "Open Declaration". Once you have "xilisf.c" open do the following:

1. Somewhere near the top of xilisf.c add the following:

#define XISF_MACRONIX_DEV_MX25L3233F    0x2016  /**< Device ID for MX25L3233F */

2. In xilisf.c find the definition for IntelStmDevices[] and add the following:

{XISF_MANUFACTURER_ID_MACRONIX, XISF_MACRONIX_DEV_MX25L3233F,
          XISF_BYTES256_PER_PAGE, XISF_PAGES256_PER_SECTOR,
          XISF_NUM_OF_SECTORS64},

3. Save xilisf.c, which should rebuild the project.

Once you've completed the above steps you need to go through the process of generating a download.bit that has integrated the bootloader elf with the bitstream, program that to the flash at offset 0, and then program your application SREC to the flash at the appropriate offset. I placed my SREC at offset 0x00180000 (same offset I set in blconfig.h of the srec bootloader project) since my bitstream was only ~1100KB. You may need to use a different offset depending on the size of your bitstream+bootloader elf combination.

I suggest that anyone who is unfamiliar with the SREC bootloader process follow the guide linked below but select "Macronix" for the Slave Device and perform the xilisf.c modifications.

https://reference.digilentinc.com/learn/programmable-logic/tutorials/htsspisf/start

Thanks
Michael

Link to post
Share on other sites
  • 0

For the curious.

And when the next production run needs a different part that isn't a 'drop in' replacement for either of the two previous FLASH devices then what? Will there be an identification scheme to tell the modules apart at the time of sale? Will there be a change notice, which is a typical practice, published before new modules enter distribution?

If I were going to use an OEM component in a product I'd want this information. I'd want this information even if I decided to buy another CMOD for the lab. My time and costs matter to me and should matter to anyone wanting to sell me stuff, especially in quantity.

Link to post
Share on other sites
  • 0
19 hours ago, malexander said:

@Kyle_Jackson

I was able to get the SREC bootloader working on a Cmod A7 - 35T that has the Macronix flash using Vivado 2019.1. To get this to work you have to include the Xilinx QSPI controller in your Microblaze block design and in the IP configuration either select "Standard" for the mode (SPI x1 mode) or if you select "QUAD" then make sure to select "Macronix" for Slave Device.

Once you are in SDK and have created the SREC bootloader project (and associated BSP) you need to open "bootloader.c" from the sources and scroll down until you find the call to "XIsf_Initialize". Double click the function name to highlight it, right click and select "Open Declaration". Once you have "xilisf.c" open do the following:

1. Somewhere near the top of xilisf.c add the following:

#define XISF_MACRONIX_DEV_MX25L3233F    0x2016  /**< Device ID for MX25L3233F */

2. In xilisf.c find the definition for IntelStmDevices[] and add the following:

{XISF_MANUFACTURER_ID_MACRONIX, XISF_MACRONIX_DEV_MX25L3233F,
          XISF_BYTES256_PER_PAGE, XISF_PAGES256_PER_SECTOR,
          XISF_NUM_OF_SECTORS64},

3. Save xilisf.c, which should rebuild the project.

Once you've completed the above steps you need to go through the process of generating a download.bit that has integrated the bootloader elf with the bitstream, program that to the flash at offset 0, and then program your application SREC to the flash at the appropriate offset. I placed my SREC at offset 0x00180000 (same offset I set in blconfig.h of the srec bootloader project) since my bitstream was only ~1100KB. You may need to use a different offset depending on the size of your bitstream+bootloader elf combination.

I suggest that anyone who is unfamiliar with the SREC bootloader process follow the guide linked below but select "Macronix" for the Slave Device and perform the xilisf.c modifications.

https://reference.digilentinc.com/learn/programmable-logic/tutorials/htsspisf/start

Thanks
Michael

A quick note here. The source files that need editing are part of a library (https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841939/xilisf) compiled into a bsp/domain. By navigating to the source files in SDK/Vitis, the local copy of the library's source files are opened. Any modification is done on this local copy and build into the static library (and application) upon build. However, when the source files of the bsp/domain are re-generated (context menu, Regenerate BSP sources), the changes are overwritten from the originals in the Xilinx install directory. Take care this does not happen.

A more permanent solution is forking the library repository (https://github.com/Xilinx/embeddedsw) editing the sources there and including a path to the modified repository in SDK/Vitis Xilinx->Repositories->Global.

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now