flash_m25p.py 8.48 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# Author: Theodor Stana <theodor.stana@gmail.com>
# Date: October 2014
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you may find one here:
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# or you may search the http://www.gnu.org website for the version 2 license,
# or you may write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
#

import sys
23
import logging
24
import struct
25
import etherbone
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
sys.path.append("eb")
from eb import *
from comm_type import *

class FlashM25P:

    # Constructor
    # comm_params -- communication parameters defining details about how this
    # class should interface to the M25P flash chip
    def __init__(self, *comm_params):
        self.comm = comm_params[0]
        if (self.comm == ELMA_I2C_MULTIBOOT):
            self.elma = comm_params[1]
            self.slot = comm_params[2]
            self.mb_base = comm_params[3]
        elif (self.comm == ETHERBONE):
            self.ebone = comm_params[1]
            self.mb_base = comm_params[2]

    # Low-level SPI transfer using communication type defined in the constructor
    #
    # This method handles the OR-ing together of the data bytes and the control
    # byte in FAR.
    #
    # Formatting of data bytes in the FAR register should be done outside this
    # function.
    def spi_transfer(self, nbytes, cs, dat):
        # Communication type is ELMA I2C, therefore use MultiBoot logic on the
        # FPGA to access the flash
        retval = 0
        wval = []
        # control byte, to be shifted left by 24
        ctrl = ((cs << 3) | 0x4) | (nbytes-1)
        if (self.comm == ELMA_I2C_MULTIBOOT):
            # Use appropriate command by type
            #
            # write - we send up to three data bytes in one FAR register write
            # writemregs - we send up to 24 data bytes in eight FAR register writes
            if isinstance(dat,int):
                self.elma.write(self.slot, self.mb_base+0x10, (ctrl << 24) | dat)
            else:
                for i in xrange(len(dat)):
                    wval.append((ctrl << 24) | dat[i])
                self.elma.writemregs(self.slot, self.mb_base+0x10, wval)

            # Read the data and prepare the return value
            while (retval & (1 << 28) == 0):
                retval = self.elma.read(self.slot, self.mb_base+0x10)
        elif (self.comm == ETHERBONE):
            if isinstance(dat,int):
                self.ebone.write(self.mb_base+0x10, (ctrl << 24) | dat)
            else:
                for i in xrange(len(dat)):
                    wval.append((ctrl << 24) | dat[i])
80 81 82
                #self.ebone.writemregs(self.mb_base+0x10, wval)
                for v in wval:
                    self.ebone.write(self.mb_base+0x10, v)
83

84
            # Read the data and prepare the return value
85 86 87 88 89 90 91 92 93
            while (retval & (1 << 28) == 0):
                retval = self.ebone.read(self.mb_base+0x10)

            return retval & 0xffffff

    # This function is used to write data bytes to flash. It calls self.spi_transfer
    # and sends the commands to the flash chip. You can follow the logic of this
    # function by looking at the sequence of the write command in [1]
    def write(self, addr, dat):
94
        logging.debug('Writting to address 0x%x' % addr)
95 96 97
        # write enable cmd
        self.spi_transfer(1,1,0x06)
        self.spi_transfer(1,0,0)
98 99 100 101 102 103 104 105 106
        # check status (WIP, WEL) bits
        status = 0
        retry = 3
        while (status != 0x2):
            if (retry <= 0):
                raise IOError('Error writing, status not vaild: 0x%x'% status)
            logging.debug('retry = %d' % retry)
            retry -= 1
            status = self.rsr()
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

        # write page cmd
        self.spi_transfer(1,1,0x02)
        # send address in reverse order
        self.spi_transfer(3,1,self.rev_addr(addr))

        # Now, massage the data array into 3-byte commands sent in 32-bit
        # registers, in batches of eight registers (writemregs command).
        i = 0
        l = len(dat)
        while (l):
            wval = []
            # If we have more than 24 bytes left in the data array,
            # we can use the full writemregs
            if (int(l/24)):
                for j in xrange(0, 8):
                    wval.append((dat[(i+2)+3*j] << 16) | \
                            (dat[(i+1)+3*j] << 8) | dat[i+3*j])
                self.spi_transfer(3, 1, wval)
                i += 24;
                l -= 24;

            # When we're left to fewer than 24 bytes in the array,
            # try to use one more writemregs with as many regs as possible
            elif (int(l/3)):
                for j in xrange(0, l/3):
                    wval.append((dat[(i+2)+3*j] << 16) | \
                            (dat[(i+1)+3*j] << 8) | dat[i+3*j])
                self.spi_transfer(3,1,wval);
                i += 3 * int(l/3)
                l -= 3 * int(l/3)

            # When we're down to less than three bytes, just use simple one-byte xfers
            elif (l):
                for j in xrange(0, l):
                    self.spi_transfer(1,1,dat[i])
                    i += 1
                    l -= 1

        self.spi_transfer(1,0,0)


    # This function is used to read a number of data bytes from the flash memory
    # It returns the values of the three consecutive flash registers packed into
    # one 24-bit data value in little-endian order
    def read(self, addr, nrbytes):
153
        logging.debug('Reading from 0x%x...' % addr)
154
        d = ''
155 156 157 158
        self.spi_transfer(1,1,0x0b)

        # send address in reverse order
        self.spi_transfer(3,1,self.rev_addr(addr))
Qiang Du's avatar
Qiang Du committed
159
        self.spi_transfer(1,1,0)
160 161 162 163 164 165 166 167 168
        n = nrbytes
        # Read bytes in groups of three
        while (n/3>0):
            d += struct.pack('L',self.spi_transfer(3,1,0))[0:3]
            n -= 3

        while (n>0):
            d += struct.pack('L',self.spi_transfer(1,1,0))[3]
            n -= 1
169 170

        self.spi_transfer(1,0,0)
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
        return d

    # xxx broken
    def page_read(self, addr):
        logging.debug('Reading from 0x%x...' % addr)
        ret = []
        wval = []
        addr = self.mb_base + 0x10
        for i in xrange(256):
            wval.append((0xc << 24) | 0)

        with etherbone.Cycle(self.ebone.device, 0, 0) as cycle:
            cycle.write(addr, (0xc<<24|0xb))
            cycle.write(addr, (0xe<<24|self.rev_addr(addr)))
            for v in wval:
                cycle.write(addr, v)
            ret = cycle.read_fast(addr, 256)
            cycle.write(addr, (0x8<<24))

        # send address in reverse order

        #self.spi_transfer(1,0,0)
193 194 195 196 197

        return ret

    # Send a sector erase command
    def serase(self, addr):
198
        logging.debug('Sector Erasing from 0x%x'% addr)
199 200 201 202 203 204
        self.spi_transfer(1,1,0x06)
        self.spi_transfer(1,0,0)
        self.spi_transfer(1,1,0xD8)
        # send address in reverse order
        self.spi_transfer(3,1,self.rev_addr(addr))
        self.spi_transfer(1,0,0)
205 206 207 208 209
        # wait until finish
        status = 1
        while (status != 0x0):
            logging.debug('Sector erasing...')
            status = self.rsr()
210 211 212 213 214 215 216 217 218 219 220 221 222

    # Send a block erase command
    def berase(self):
        self.spi_transfer(1,1,0x06)
        self.spi_transfer(1,0,0)
        self.spi_transfer(1,1,0xc7)
        self.spi_transfer(1,0,0)

    # Read status register command
    def rsr(self):
        self.spi_transfer(1,1,0x05)
        ret = self.spi_transfer(1,1,0)
        self.spi_transfer(1,0,0)
223
        logging.debug('Status Register: 0x%x'% ret)
224 225 226 227
        return ret

    def read_id(self):
        ret = []
228
        d = ''
229
        self.spi_transfer(1,1,0x9f)
230 231 232 233
        for i in range(6):
            d += struct.pack('L',self.spi_transfer(3,1,0))[0:3]
        for i in range(2):
            d += struct.pack('L',self.spi_transfer(1,1,0))[3]
234
        self.spi_transfer(1,0,0)
235
        return d
236 237 238 239 240

    def rev_addr(self, addr):
        rev_addr = ((addr & 0xff0000) >> 16) | (((addr & 0xff00) >> 8) << 8) | \
            ((addr & 0xff) << 16)
        return rev_addr