Altera ECPQ flash access with a Nios II processor + programming bitfiles

This post was written by eli on April 23, 2017
Posted Under: FPGA,Intel FPGA (Altera)

Introduction

This post outlines some technical details on accessing an Altera ECPQ flash from a Nios II processor for read, write and erase. A non-OS settings (“bare metal”) setting is assumed.

And as a bonus (at the bottom of this post), how to program the flash based upon a SOF file, both with JTAG and by writing directly.

Remote Update is discussed in this post.

Hardware setup

In the Qsys project, there should be an instance of the Legacy EPCS/EPCQx1 Flash Controller, configured with the default parameters (not that there is much to configure). The peripheral’s epcs_control_port should be connected to the Nios II’s data master Avalon port (no point connecting it to the instruction master too).

In this example, we’ll assume that the name of Flash Controller in Qsys is epcs_flash_controller_0.

The interrupt signal isn’t used in the software setting given below, but as the connection to the Nios processor, as well as the interrupt number assignment is automatic, let it be.

Clock and reset — like the other peripherals.

The external conduit is connected as follows to an ECPQ flash, for a x1 access:

  • Flash pin DATA0 to epcs_flash_controller_0_sdo (FPGA pin ASDO)
  • Flash pin DCLK to epcs_flash_controller_0_dclk (FPGA pin DCLK)
  • Flash pin nCS to epcs_flash_controller_0_sce (FPGA pin NCSO)
  • Flash pin DATA1 to epcs_flash_controller_0_data (FPGA pin DATA0)

The FPGA pins above relate to dual-use of the configuration, which allows the FPGA to configure in Active Serial (AS) x 1 mode. Once the configuration is done, these pins become general-purpose I/O (when so required by assignments), which allows regular access to the flash device.

Note that the flash pin DATA1 is connected to the FPGA pin DATA0 — this is not a mistake, but the correct wiring for AS x 1 interface.

It’s of course possible to connect the flash to regular I/O pins, but then the FPGA won’t be able to configure from the flash.

Software

Altera’s BSP includes drivers for flash operations with multiple layers of abstraction. This abstraction is not always necessary, and makes it somewhat difficult to figure out what’s going on (in particular when things go wrong). In particular, the higher-level drivers erase flash sectors automatically before writing, which can render some counterintuitive behavior, for example if multiple write requests are made on the same sector.

I therefore prefer working with the lowest-level drivers, which merely translate the flash commands into SPI communication. It leaves the user with the responsibility to erase sectors before writing to them.

The rule is simple: The flash is divided into sectors of 64 kB each. An erase operation is performed on such 64 kB sector, leaving all its bytes in all-1′s (all bytes are 0xff).

Writing can then be done to arbitrary addresses, but effectively the data in the flash is the written data ANDed with the previous content of the memory cells. Which means a plain write, if the region has been previously erased. It’s commonly believed that it’s unhealthy for the flash to write to a byte cell twice without an erase in the middle.

This is a simple program that runs on the Nios II processor, which demonstrates read, write and erase.

#include <system.h>
#include <alt_types.h>
#include <io.h>
#include "sys/alt_stdio.h"
#include "epcs_commands.h"

static void hexprint(alt_u8 *buf, int num) {
  int i;

  const char hexes[] = "0123456789abcdef";

  for (i = 0; i < num; i++) {
    alt_putchar(hexes[(buf[i] >> 4) & 0xf]);
    alt_putchar(hexes[buf[i] & 0xf]);
    if ((i & 0xf) == 0xf)
      alt_putchar(10); // "\n"
    else
      alt_putchar(32); // " "
  }
  alt_putchar(10); // "\n"
}

int main()
{
  alt_u32 register_base = EPCS_FLASH_CONTROLLER_0_BASE + EPCS_FLASH_CONTROLLER_0_REGISTER_OFFSET;
  alt_u32 silicon_id;

  alt_u8 buf[256];
  alt_u32 junk = 0x12345678;
  const alt_u32 flash_address = 0x100000;

  silicon_id = epcs_read_device_id(register_base);

  alt_printf("ID = %x\n", silicon_id);

  // epcs_read_buffer always returns the length of the buffer, so no
  // point checking its return value.

  alt_printf("Before doing anything:\n");

  epcs_read_buffer(register_base, flash_address, buf, sizeof(buf), 0);
  hexprint(buf, 16);

  // epcs_sector_erase erases the 64 kiB sector that contains the address
  // given as its second argument, and waits for the erasure to complete
  // by polling the status register and waiting for the WIP (write in progress)
  // bit to clear.

  epcs_sector_erase(register_base, flash_address, 0);

  alt_printf("After erasing\n");

  epcs_read_buffer(register_base, flash_address, buf, sizeof(buf), 0);
  hexprint(buf, 16);

  // epcs_write_buffer must be used on a region previously erased. The
  // command waits for the operation to complete by polling the status
  // register and waiting for the WIP (write in progress) bit to clear.
  epcs_write_buffer(register_base, flash_address, (void *) &junk, sizeof(junk), 0);

  alt_printf("After writing\n");

  epcs_read_buffer(register_base, flash_address, buf, sizeof(buf), 0);
  hexprint(buf, 16);

  /* Event loop never exits. */

  while (1);

  return 0;
}

The program reads 256 bytes each time, even though only 16 bytes are displayed. Any byte count is allowed in read and write. Needless to say, flash_address can be changed to any address in the device’s range. The choice of 0x100000 kept it off the configuration bitstream for the relevant FPGA.

This is the output of the program above running against an EPCQ16:

ID = 20ba15
Before doing anything:
78 56 34 12 ff ff ff ff ff ff ff ff ff ff ff ff

After erasing
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

After writing
78 56 34 12 ff ff ff ff ff ff ff ff ff ff ff ff

The data in the “Before doing anything” part can be anything that was left in the flash when the program ran. In the case above, it’s the results of the previous run of the same program.

As a side note, all EPCQ flashes also support erasing subsectors, each of 4 kiB size (hence 16 subsectors per sectors). Altera’s low-level drivers don’t support subsector erase, but it’s quite easy to expand the code to do so.

Programming the flash with a SOF file

As promised, here’s the outline of how to program the EPCQ flash with a bitstream configuration file. Not as fancy as the topic above, but nevertheless useful. The flash needs to be connected as follows:

  • Flash pin DATA0 to FPGA pin ASDO
  • Flash pin DCLK to FPGA pin DCLK
  • Flash pin nCS to FPGA pin NCSO
  • Flash pin DATA1 to FPGA pin DATA0 (once again, this is not a mistake. DATA1 to DATA0 indeed)

First thing first: Generate a JIC file. Command-line style, e.g.:

quartus_cpf -c -d EPCQ16 -s EP4CE15 projname.sof projname.jic

In the example above, the EPCQ16 argument is the flash device, and the EP4CE15 is the FPGA that will be used to program the flash, which is most likely the same FPGA the SOF targets.

Or do it with GUI:

  • In Quartus, pick File > Convert Programming File…
  • Choose jic output file format, and set the output file name.
  • Set the configuration device to e.g. EPCQ16, Active Serial (not x4).
  • Pick the SOF Data row, Page_0, click Add File… and pick SOF file.
  • Pick the Flash Loader and click Add Device…, and choose e.g. Cyclone IV E, and then the same device as listed for the SOF file.
  • If you want to write to the flash with your own utility, check “Create Config data RPD”
  • Click Generate. A window saying the JIC file has been generated successfully should appear.
  • Click Close to close this tool.

Programming the flash with JTAG:

  • Open the regular JTAG programmer in Quartus (not the one in Eclipse). The one used to configure the FPGA via JTAG with a bitstream, that is.
  • Click Add File… and select the JIC file created above.
  • The FPGA with its flash attached should appear in the diagram part of the window.
  • Select the Program/Configure checkbox on the flash’ (e.g. EPCQ16) row
  • Click Start.
  • This should take some 10 seconds or so (for EP4CE15′s bitfile), and end successfully.
  • The flash is now programmed.

Note that there’s an “Erase” checkbox on the flash’ row — there is no need to enable it along with Program/Configure, and neither is it necessary. The Programmer gets the hint, and erases the flash before programming it.

Programming the flash with NIOS software (or similar)

Note that I have another post focusing on remote update.

To program the flash with your own utility, make sure that you’ve checked “Create Config data RPD” when generating the JIC. Then, using the flash API mentioned above, copy the RPD file into the flash from address 0 to make it load when the FPGA powers up, or to a higher address for using the bitstream with a Remote Update core (allowing configuration from higher addresses).

And note the following, which relates to my experience with using the EPCQ16 flash for AS configuring an Cyclone IV E FPGA, and running Quartus Prime Version 15.1.0 Build 185 (YMMV):

  • Bit reversal is mandatory if epcs_write_buffer() is used for writing to the flash (or any other Nios API, I suppose). That means that for each byte in the RPD file, move bit 7 to bit 0, bit 6 to bit 1 etc. There are small hints of bit reversal spread out in the docs, for example, in the “Read Bytes Operation” section of the Quad-Serial Configuration (EPCQ) Devices Datasheet.
  • All my attempts to generate RBF or RPD files in other ways, including using the command line tool (quartus_cpf) to create an RBF from the SOF or an RPD from a POF failed. That is, I got RBF and RPD files, but they slightly different from the file that eventually worked. In particular, the RBF file obtained with
    quartus_cpf -c project.sof project.rbf

    was almost identical to the RPD file that worked properly, with a few bytes different in the 0x20-0x4f positions of the files. And that difference probably made the FPGA refuse to configure from it. Go figure.

  • If you’re really into generating the flash image with command line tools, generate a COF file (containing the configuration parameters) with the GUI, and use it with something like
    quartus_cpf -c project.cof

    The trick about this COF is that it should generate a proper JIC file, but have the <auto_create_rpd> part set to “1″.

And finally, just a few sources I found (slightly unrelated):

  • Srunner is a command line utility for programming a EPCS flash. Since source code is given, it can give some insights, as well as its documentation.
  • The format of POF files is outlined in fmt_pof.pdf.

Using an EPCQ16A device instead

The EPCQ16 device is obsolete, and replaced with EPCQ16A. Unfortunately, the AN822 Migration Guide supplies confusing and rather discouraging information, but in the end of the day, it’s a drop-in replacement for all purposes mentioned above. Except that it replies with an ID = ef4015 instead of the 20ba15 shown above. Which is fine, because it’s only the lower 8 bits that Altera / Intel stand behind. The other 16 bits are considered junk data during “dummy clock cycles” according to the datasheet (even though they are taken seriously somewhere in Altera’s old drivers, don’t ask me where I saw it).

The Migration Guide lists different Altera IP cores related to the flash, and points at which are compatible and which are not. The Legacy Flash EPCS/EPCQx1 flash controller isn’t mentioned at all in this list, but as this controller is merely and SPI controller, it’s the opcode compatibility that matters.  According to the Migration Guide, the relevant opcodes remain the same, which is probably all that matters: The 4BYTEADDREN/4BYTEADDEX commands that are gone in EPCQA are never used (the flash writing application never requests 4-byte write), and the 0x0b / 0xeb (fast read commands) aren’t even listed in epcs_commands.h.

Bottom line: No problem using the “A” version in the usage scenarios shown above.

Reader Comments

Hi,

Thanks for the detail! this is really useful. I’m looking at doing a remote update on a Cyclone IV E. Quick question: I see you’re doing the update with the Nios II soft-core processor. Is it worth looking at doing this in fabric/user logic instead or is that just too much trouble? I see the documentation does mention using logic instead but is it worth the effort?

Thanks,
Stacey

#1 
Written By Stacey on January 18th, 2019 @ 08:44

Hello,

Anything can be done with directly on logic of course. Take a look on the underlying functions (in C) and make your own tradeoff. “Worth the effort” depends on how much logic you have to spare for the Nios processor.

#2 
Written By eli on January 18th, 2019 @ 12:06

Hello,

I’m looking for information how to connect EPCQ1024L
to NIOS II Gen2. there is constrain on the clock rate (limited to 25 MHz) so clock crossing bridge is necessary.
So, if you will update the post I will appreciate it.

Regards,

Shoval

#3 
Written By shoval on March 13th, 2019 @ 14:06

Add a Comment

required, use real name
required, will not be published
optional, your blog address