Config flash

From Hamsterworks Wiki!

Jump to: navigation, search

This |FPGA Project was started and finished in October 2012

Note This will not work on all FPGA boards - you have to be able to control all the required signals to the configuration flash. For example, on the Digilent Basys2 the CS line is tied to the "done" signal, so you can't enable the flash chip.

Most FPGA boards include a serial Flash chip to hold the configuration bitstream, which the FPGA loads on power up.

This flash is almost always larger than the FPGA needs. In the Papilio One - 250 the flash is 4 megabit, but only about 1.5 megabits are used:

Config flash1.png

You can use the excess capacity to store your own data - here's how to do it.

Contents

Reading from the flash

On the Papilio One, read requires

  • Dropping the CS line
  • Sending the read command (0x03)
  • Sending a 24 bit address,
  • Clocking out the data.

Here is the waveform from the simulator, reading from address 0x2C000:

Flash read.png


What is needed

The Papilio One-250 includes a 4Mbit flash, but only needs around 1.35Mb to for the FPGA. That leaves about 650k free - just under 80kBytes.

Two problems are faced in trying to make use of this data:

  1. How to get data into the ".bit" file used to program the flash
  2. How to tread the data back from within your FPGA design

The first part is poorly documented - the best I've found has been Alex's post on at http://forum.gadgetfactory.net/index.php?/page/articles.html/_/papilio/papilio-plus/bootstrapping-sram-from-flash-on-the-papilio-plus-r47 which links to a python script to merge data into the bitstream, based on data in the FPGA faq http://www.fpga-faq.com/FAQ_Pages/0026_Tell_me_about_bit_files.htm. I've re-implemented it in 'C', and given it a little more flexibility. I've also written a little VHDL design to read the data from the flash and display it on the LogicStart's LEDs.

The second part is well documented in the flash chip's datasheet.

The following files are needed for the entire project:

File Description
bitmerge.c Program to merge a datafile into a bitstream at the desired address
lights.c Program to create a little binary data file to merge
make.bat Batch file to merge the data into the bitstream and call the programmer
config_flash.vhdl Design to read back from the config flash
config_flash.ucf Constraints file for the Papilio One + LogicStart WegaWing

bitmerge.c

Here's a short program to extend a '.bit' file, and include arbitrary binary data in it. It compiles under MS Visual C++ as a console application.

/************************************************************************
* bitmerge.cpp : Merges a binary file into an Xilinx bistream
* Author       : Mike Field <hamster@snap.net.nz>
* Usage        : bitmerge bitstream.bit  2F00:data.bin merged.bit
*
* Adds the contents of "data.bin" into the bitsteam at address 2F00
*
* It is up to you to make sure that the resulting bitstream will 
* fit inside you FPGA's configuration flash!
*
* Inspired by Alex's bitmerge.py on http://Papilio.cc
*
* Please feel free to use however you please, but it is supplied "as is, 
* where is"
*
************************************************************************/
#include "stdafx.h"   /* For Windows */
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

static unsigned char header[] = { 0, 9 , 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0};

unsigned char *buffer;
int inlen,binlen;

static int error(char *message) {
   fprintf(stderr, "ERROR: %s\n", message);
   exit(2);
}


static void checkTextSection(char tag, int *inptr)
{
   unsigned len;
   if(*inptr > inlen-3) error("Section too short");
   if(buffer[*inptr] != tag) error("Unexpected section tag");
   len = (buffer[*inptr+1]<<8)+buffer[*inptr+2];
   // printf("Section '%c' (size %6d) '%*.*s'\n", buffer[*inptr], len, len-1, len-1, buffer+*inptr+3);
   *inptr += len+3;
}

int main(int argc, char *argv[])
{
   FILE *f;
   int inptr = 0, align = 0, padding, bitstreamLen, bitstreamAddr;

   if(argc != 4) {
      fprintf(stderr,"Usage: %s input.bit hex_address:binary_file output.bit\n\n"
             "Concatenates a binary file into an Xilinx FPGA bitstream\n",argv[0]);
      return 1;
   }

   while(1) {
      char c = argv[2][0];
      argv[2]++;
      if(c >= '0' && c <= '9')      align = align * 16 + c - '0';
      else if(c >= 'A' && c <= 'F') align = align * 16 + c - 'A'+10;
      else if(c >= 'a' && c <= 'f') align = align * 16 + c - 'a'+10;      
      else if(c == ':') break;
      else error("Expected a hex address followed by a colon before binary file name");
   }

   if(NULL == (f = fopen(argv[1],"rb"))) error("Unable to open input .bit file");
   /* Work out the length of the files */
   fseek(f,0,SEEK_END);
   if((inlen = ftell(f)) == 0) error("Input file is too short");
   fseek(f,0,SEEK_SET);

   /* Read in the input file */
   if((buffer = (unsigned char *)malloc(inlen)) == NULL) error("Out of memory");
   if(fread(buffer, inlen, 1, f) != 1)               error("Unable to read input file");
   fclose(f);

   /* Check that the expected magic numbers are in the header */
   if(memcmp(buffer,header, sizeof(header))!=0)    error("Invalid .bit file header");
   inptr = sizeof(header);

   if((buffer[inptr]<<8)+buffer[inptr+1] != 1) error("expecting a '1'");
   inptr+=2;

   checkTextSection('a',&inptr);
   checkTextSection('b',&inptr);
   checkTextSection('c',&inptr);
   checkTextSection('d',&inptr);

   if(inptr >= inlen - 5)
   if(buffer[inptr] != 'e') error("Expected section 'e', got something else");

   bitstreamAddr = inptr+5;
   bitstreamLen = (buffer[inptr+1]<<24) + (buffer[inptr+2]<<16) + (buffer[inptr+3]<<8) + (buffer[inptr+4]);
   printf("\nFPGA Bitstream is %i bytes (%i bits)\n", bitstreamLen, bitstreamLen*8);
   inptr+=5;

   if(bitstreamAddr + bitstreamLen != inlen)  error("File length differs from the expected");

   /* Pad the bitstream out with zeros to the address */
   padding = align-bitstreamLen;
   if(padding < 0)   error("Data location will overwrite FPGA bitstream");
   if(padding > 0) {
      printf("Padding bitstream with %i zero bytes\n", padding);
      if((buffer = (unsigned char *)realloc(buffer,inlen+padding)) == NULL) error("Out of memory");
      memset(buffer+inlen,0,padding);
      inlen += padding;
      bitstreamLen += padding;
   }

   /* Append the binary data to the padded bitstream */
   if(NULL == (f = fopen(argv[2],"rb"))) error("Unable to open data .bit file");
   fseek(f,0,SEEK_END);
   if((binlen = ftell(f))== 0) error("Binary file is too short");
   fseek(f,0,SEEK_SET);
   printf("Appending %i bytes of data to the bitstream at address %x\n",binlen, align);

   if((buffer = (unsigned char *)realloc(buffer,inlen+binlen)) == NULL) error("Out of memory");
   if(fread(buffer+inlen, binlen, 1, f) != 1) error("Unable to read binary file");
   fclose(f);
   inlen += binlen;
   bitstreamLen += binlen;

   /* backpatch the length of the updated bitstream */
   buffer[bitstreamAddr-4] = (unsigned char)(bitstreamLen>>24);
   buffer[bitstreamAddr-3] = (unsigned char)(bitstreamLen>>16);
   buffer[bitstreamAddr-2] = (unsigned char)(bitstreamLen>>8);
   buffer[bitstreamAddr-1] = (unsigned char)(bitstreamLen>>0);

   if((f = fopen(argv[3],"wb")) == NULL) error("Unable to open output file");
   if(fwrite(buffer,inlen,1,f) != 1) error("Unable to write file\n");
   fclose(f);
   printf("Written new file with a bitstream of %i bytes (%i bits)\n", bitstreamLen, bitstreamLen*8);
   return 0;
}

lights.c

This generates a test pattern to store in flash. It flashes the LEDs a few times, then has a "Knight rider" LED chaser.

/**************************
* lights.c : Generates a binary file used to 
*            test storing data in configuraiton flash
*/

#include "stdafx.h"  /* For Windows */
#include "stdio.h"

unsigned char l2r[]   = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
unsigned char r2l[]   = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
unsigned char flash[] = { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00 };

int main(int argc, char *argv[])
{
   FILE *f;
   if(argc != 2 )
   {
      printf("Usage: %s filename\n\nGenerate a test pattern for lights\n");
      return 1;
   }
   f = fopen(argv[1],"wb");
   if(f ==NULL) {
      printf("Unable to open output file\n");
      return 2;
   }
   for(i = 0; i < 2; i++)
      fwrite(flash,sizeof(flash),1,f);

   for(i = 0; i < 2; i++) {
      fwrite(l2r,sizeof(l2r),1,f);
      fwrite(r2l,sizeof(r2l),1,f);
   }

   for(i = 0; i < 2; i++)
      fwrite(flash,sizeof(flash),1,f);
   fclose(f);
   return 0;
}

make.bat

A little batch file to create the data.bin file, merge it into the bitstream, and then upload it to the Papilio One.

Once uploaded, the flash will look like this:

Config flash2.png

lights.exe data.bin
bitmerge.exe config_flash.bit 2C000:data.bin cfout.bit
cfout.bit

config_flash.vhdl

This program displays the contents of the Flash from address 0x02C000 on the LEDs, at about 8 bytes per second.

If you wait long enough it will display the whole flash over and over again.

----------------------------------------------------------------------------------
-- Name: config_flash.vhd
-- Engineer: Mike Field <hamster@snap.net.nz>
-- Description: Reads data from the config flash and displays on the LEDs
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity config_flash is
    Port ( clk        : in  STD_LOGIC;
           flash_sclk : out STD_LOGIC;
           flash_cs   : out STD_LOGIC;
           flash_so   : in  STD_LOGIC;
           flash_si   : out STD_LOGIC;
           leds       : out STD_LOGIC_VECTOR (7 downto 0));
end config_flash;

architecture Behavioral of config_flash is
  constant read_cmd    : std_logic_vector(7 downto 0)  := x"03";
  constant rom_address : std_logic_vector(23 downto 0) := x"02C000";

  -- lower CE after 6 cycles
  signal ce_sr   : std_logic_vector(5 downto 0)  := "111111";

  -- clock starts after 8 cycles
  signal sclk_sr : std_logic_vector(7 downto 0)  := "00000000";
  signal data_sr : std_logic_vector(7 downto 0)  := "00000000";

  -- command bit gets changed every other clock transition
  signal cmd_sr  : std_logic_vector(31 downto 0) := read_cmd & rom_address;

  -- counter to keep track of when we have data - first byte is available after 40 ticks
  signal clocks_to_byte : unsigned(5 downto 0) := "100111";

  -- counter used to pause reading for a about second once each byte is read
  signal pause : unsigned(21 downto 0) := (others => '1');

begin
   flash_cs   <= ce_sr(5);
   flash_sclk <= sclk_sr(7);
   flash_si   <= cmd_sr(31);

   process(clk)
   begin
      if rising_edge(clk) then
         if pause /= 0 then
            pause <= pause -1;
         else
            ce_sr   <= ce_sr(4 downto 0) & '0';
            sclk_sr <= sclk_sr(6 downto 0) & not sclk_sr(0);
   
            if sclk_sr(7) = '1' then
               cmd_sr <= cmd_sr(30 downto 0) &'0';
            -- do we have a complete byte to output?
               if clocks_to_byte = "000000" then
                  leds           <= data_sr;
                  clocks_to_byte <= "000111";
                  pause          <= (others => '1'); -- 32*1024*1024-1
               else
                  clocks_to_byte <= clocks_to_byte-1;
               end if;
            else
               data_sr <= data_sr(6 downto 0) &  flash_so;
            end if;
         end if;
      end if;
   end process;
end Behavioral;

config_flash.ucf

Constraints for the Papilio One + LogicStart Megawing

NET "clk" TNM_NET = clk;
TIMESPEC TS_clk = PERIOD "clk" 20 ns HIGH 50%;

#####################################################
# Papilio One + LogicStart 
#####################################################
NET "clk"      LOC="P89"   | IOSTANDARD=LVTTL | PERIOD=31.25ns;
NET LEDs(7)    LOC = "P5"  | IOSTANDARD=LVTTL;
NET LEDs(6)    LOC = "P9"  | IOSTANDARD=LVTTL;
NET LEDs(5)    LOC = "P10" | IOSTANDARD=LVTTL;
NET LEDs(4)    LOC = "P11" | IOSTANDARD=LVTTL;
NET LEDs(3)    LOC = "P12" | IOSTANDARD=LVTTL;
NET LEDs(2)    LOC = "P15" | IOSTANDARD=LVTTL;
NET LEDs(1)    LOC = "P16" | IOSTANDARD=LVTTL;
NET LEDs(0)    LOC = "P17" | IOSTANDARD=LVTTL;
NET FLASH_CS   LOC = "P24" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST ;
NET FLASH_SCLK LOC = "P50" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST ;
NET FLASH_SI   LOC = "P27" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST ;
NET FLASH_SO   LOC = "P44" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST | PULLUP;

Personal tools