adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.11k stars 1.22k forks source link

SPI Flash Issue - SST26VF016B drive not showing #1530

Closed wallarug closed 5 years ago

wallarug commented 5 years ago

Hi Adafruit Community!

I have been trying to make a custom CircuitPython package for a new board that I have created based around the M0. I am having issues getting the CIRCUITPY USB drive to show up for CircuitPython when attached to a USB port.

COM port is present and working (can log into CircuitPython and use it).

I am using a flash chip that does not appear to be supported by CircuitPython or I am making a mistake somewhere. It is using the SST26VF016B which should be a drop in replacement for the supported S25FL116K 2MiB flash chip.

I can only get the USB drive to show if I use "internal flash". The pre-compiled firmware for Feather M0 Basic works perfectly. The Feather M0 Express does not (even though I used the same SPI pin out as this board).

I have attached the files that I used to customize the CircuitPython install for my board (MM1 HAT).

I do not normally work with such a low level of commands / hardware. I love CircuitPython though!

Any assistance in identifying why the USB drive does not appear would be greatly appreciated!!

Thank you in advance.

Appendix

modified supervisor/shared/external_flash/devices.h

. . .
// Settings for the SST26VF016B 2MiB SPI flash.
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20005262D.pdf
#define ST26VF016B {\
    .total_size = (1 << 21), /* 2 MiB */ \
    .start_up_time_us = 10000, \
    .manufacturer_id = 0xbf, \
    .memory_type = 0x26, \
    .capacity = 0x15, \
    .max_clock_speed_mhz = 104, \
    .quad_enable_bit_mask = 0x02, \
    .has_sector_protection = false, \
    .supports_fast_read = true, \
    .supports_qspi = true, \
    .supports_qspi_writes = false, \
    .write_status_register_split = false, \
    .single_status_byte = false, \
}
. . .

ports/at-samd/

Build by:

make -j8 BOARD=mm1_hat

boards/mm1_hat/board.c

#include "boards/board.h"

void board_init(void)
{
}

bool board_requests_safe_mode(void) {
    return false;
}

void reset_board(void) {
}

boards/mm1_hat/mpconfigboard.h

#define MICROPY_HW_BOARD_NAME "Robotics Masters MM1 HAT"
#define MICROPY_HW_MCU_NAME "samd21g18"

#define MICROPY_HW_LED_STATUS   (&pin_PA12)

//#define MICROPY_HW_NEOPIXEL (&pin_PA06)

// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz.
#define SPI_FLASH_BAUDRATE  (8000000)

// On-board flash
#define SPI_FLASH_MOSI_PIN          &pin_PA08
#define SPI_FLASH_MISO_PIN          &pin_PA14
#define SPI_FLASH_SCK_PIN           &pin_PA09
#define SPI_FLASH_CS_PIN            &pin_PA13

// These are pins not to reset.
#define MICROPY_PORT_A        ( 0 ) //PORT_PA06
#define MICROPY_PORT_B        ( 0 )
#define MICROPY_PORT_C        ( 0 )

// If you change this, then make sure to update the linker scripts as well to
// make sure you don't overwrite code.
#define CIRCUITPY_INTERNAL_NVM_SIZE 256

#define BOARD_FLASH_SIZE (0x00040000 - 0x2000 - CIRCUITPY_INTERNAL_NVM_SIZE)

//#define BOARD_HAS_CRYSTAL 0
#define CALIBRATE_CRYSTALLESS 1

#define DEFAULT_I2C_BUS_SCL (&pin_PA23)
#define DEFAULT_I2C_BUS_SDA (&pin_PA22)

#define DEFAULT_SPI_BUS_SCK (&pin_PA05)
#define DEFAULT_SPI_BUS_MOSI (&pin_PA07)
#define DEFAULT_SPI_BUS_MISO (&pin_PA06)

#define DEFAULT_UART_BUS_RX (&pin_PB23)
#define DEFAULT_UART_BUS_TX (&pin_PB22)

// USB is always used internally so skip the pin objects for it.
#define IGNORE_PIN_PA24     1
#define IGNORE_PIN_PA25     1

#define CIRCUITPY_I2CSLAVE
#define CIRCUITPY_DISPLAYIO (0)

boards/mm1_hat/mpconfigboard.mk

LD_FILE = boards/samd21x18-bootloader-external-flash.ld
USB_VID = 0x0005
USB_PID = 0x0005
USB_PRODUCT = "MM1 HAT"
USB_MANUFACTURER = "Robotics Masters"

SPI_FLASH_FILESYSTEM = 1
EXTERNAL_FLASH_DEVICE_COUNT = 1
EXTERNAL_FLASH_DEVICES = ST26VF016B
LONGINT_IMPL = MPZ

CHIP_VARIANT = SAMD21G18A
CHIP_FAMILY = samd21

CFLAGS_INLINE_LIMIT = 55

# Include these Python libraries in firmware.
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Crickit
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Motor
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_seesaw

boards/mm1_hat/pins.c

#include "shared-bindings/board/__init__.h"

#include "supervisor/shared/board_busses.h"

STATIC const mp_rom_map_elem_t board_global_dict_table[] = {    
    // SIGNAL I/O Pins (Primary)
    { MP_ROM_QSTR(MP_QSTR_SIGNAL1), MP_ROM_PTR(&pin_PB02) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL2), MP_ROM_PTR(&pin_PB03) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL3), MP_ROM_PTR(&pin_PB08) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL4), MP_ROM_PTR(&pin_PB09) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL5), MP_ROM_PTR(&pin_PA15) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL6), MP_ROM_PTR(&pin_PA18) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL7), MP_ROM_PTR(&pin_PB22) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL8), MP_ROM_PTR(&pin_PB23) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL9), MP_ROM_PTR(&pin_PA02) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL10), MP_ROM_PTR(&pin_PA03) },
    { MP_ROM_QSTR(MP_QSTR_SIGNAL11), MP_ROM_PTR(&pin_PA12) },

    // SERVO Pins (Primary)
    { MP_ROM_QSTR(MP_QSTR_SERVO1), MP_ROM_PTR(&pin_PB10) },
    { MP_ROM_QSTR(MP_QSTR_SERVO2), MP_ROM_PTR(&pin_PB11) },
    { MP_ROM_QSTR(MP_QSTR_SERVO3), MP_ROM_PTR(&pin_PA20) },
    { MP_ROM_QSTR(MP_QSTR_SERVO4), MP_ROM_PTR(&pin_PA21) },
    { MP_ROM_QSTR(MP_QSTR_SERVO5), MP_ROM_PTR(&pin_PA10) },
    { MP_ROM_QSTR(MP_QSTR_SERVO6), MP_ROM_PTR(&pin_PA11) },
    { MP_ROM_QSTR(MP_QSTR_SERVO7), MP_ROM_PTR(&pin_PA16) },
    { MP_ROM_QSTR(MP_QSTR_SERVO8), MP_ROM_PTR(&pin_PA17) },

    // RC_CH Pins (Primary)
    { MP_ROM_QSTR(MP_QSTR_RCH1), MP_ROM_PTR(&pin_PA04) },
    { MP_ROM_QSTR(MP_QSTR_RCH2), MP_ROM_PTR(&pin_PA05) },
    { MP_ROM_QSTR(MP_QSTR_RCH3), MP_ROM_PTR(&pin_PA06) },
    { MP_ROM_QSTR(MP_QSTR_RCH4), MP_ROM_PTR(&pin_PA07) },

    // Special Function (Primary)
    { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_PA19) },
    { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PA22) },
    { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PA23) },
    { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_PB22) },
    { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_PB23) },

    // SPI on SERCOM4(Secondary)
    { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_PB11) },
    { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PB08) },
    { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB10) },
    { MP_ROM_QSTR(MP_QSTR_SS1), MP_ROM_PTR(&pin_PB09) },
    { MP_ROM_QSTR(MP_QSTR_SS2), MP_ROM_PTR(&pin_PB03) },

    // I2C on SERCOM1 (Secondary)
    { MP_ROM_QSTR(MP_QSTR_I2C_SDA), MP_ROM_PTR(&pin_PA16) },
    { MP_ROM_QSTR(MP_QSTR_I2C_SCL), MP_ROM_PTR(&pin_PA17) },

    // GPS on SERCOM1 (Secondary)
    { MP_ROM_QSTR(MP_QSTR_GPS_SDA), MP_ROM_PTR(&pin_PA16) },
    { MP_ROM_QSTR(MP_QSTR_GPS_SCL), MP_ROM_PTR(&pin_PA17) },
    { MP_ROM_QSTR(MP_QSTR_GPS_TX), MP_ROM_PTR(&pin_PA18) },
    { MP_ROM_QSTR(MP_QSTR_GPS_RX), MP_ROM_PTR(&pin_PA19) },

    // UART on SERCOM0 (Secondary)
    { MP_ROM_QSTR(MP_QSTR_UART_RX), MP_ROM_PTR(&pin_PA04) },
    { MP_ROM_QSTR(MP_QSTR_UART_TX), MP_ROM_PTR(&pin_PA05) },
    { MP_ROM_QSTR(MP_QSTR_UART_RTS), MP_ROM_PTR(&pin_PA06) },
    { MP_ROM_QSTR(MP_QSTR_UART_CTS), MP_ROM_PTR(&pin_PA07) },

    // Raspberry Pi (Secondary)
    { MP_ROM_QSTR(MP_QSTR_GPIO25), MP_ROM_PTR(&pin_PA30) },
    { MP_ROM_QSTR(MP_QSTR_GPIO24), MP_ROM_PTR(&pin_PA31) },
    { MP_ROM_QSTR(MP_QSTR_GPIO23), MP_ROM_PTR(&pin_PA28) },
    { MP_ROM_QSTR(MP_QSTR_GPIO16), MP_ROM_PTR(&pin_PA27) },
    { MP_ROM_QSTR(MP_QSTR_PI_RX), MP_ROM_PTR(&pin_PB22) },
    { MP_ROM_QSTR(MP_QSTR_PI_TX), MP_ROM_PTR(&pin_PB23) },

    { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
    { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
    { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
dhalbert commented 5 years ago

Page 23 of the datasheet for that chip says that the 0x9F (9FH) Read JEDEC ID command responds with0xBF, 0x26, and then 0x41. Datasheets for other chips say that the third byte describes the capacity. The datasheet does not say anywhere that the chip responds with 0x15 for anything, which is what you set the capacity to. So try this:

    .capacity = 0x41, \

There may be other issues, but that's what I see at first glance.

wallarug commented 5 years ago

Thanks @dhalbert . I'll give it a go tomorrow (It's very late down in Oz right now) and report back.

wallarug commented 5 years ago

No luck there. Is there any other reason that CircuitPython would work perfectly over serial but not show the USB Drive?

dhalbert commented 5 years ago

If it's not able to talk the SPI flash chip, then there'll be no CIRCUITPY. So I think there is still debugging to do there. You'll probably need to set some breakpoints and single step to see if it's working. And you can add print statements (since the serial is working).

tannewt commented 5 years ago

@wallarug What do you mean by works perfectly? Are you doing file system manipulations from the REPL? They usually fail as well as the mass storage.

wallarug commented 5 years ago

Hi @tannewt ,

When I say "working perfectly" it is probably an overstatement.

I can connect to CircuitPython using Putty (on WIndows) over the Serial COM port and make the LED on the board flash. File manipulations fail. I tried running the filesystem_erase and it said "incorrect pins".

REPL does not work since it relays on the USB drive appearing.

I am doing some more testing today to see if this chip can work or not.

tannewt commented 5 years ago

Ok, sounds like the issue is the flash chip initialization. One of the first steps is to make sure your chip's id is being recognized here: https://github.com/adafruit/circuitpython/blob/master/supervisor/shared/external_flash/external_flash.c#L194

I use a debugger to read the values that we're read and to make them match.

A logic analyzer can be helpful too to see how far the flash code gets.

wallarug commented 5 years ago

Hey @tannewt ,

Thank you for your helpful insight.

Please excuse my inexperience here: If the flash is mounted to the board and attached to the M0 chip, how can I read those JEDEC values? Is there a way that I can get the code you linked (which I believe is run on the M0) to output some information somewhere without needing any special equipment? -- I'm thinking quick and dirty print statements or something onto the M0 console (stdout).

At the moment I only really have access to the serial com port. Happy to burn whatever bootloader is needed and recompile the firmware.

I just need to be pointed in the right direction and then I can find my way :smile:

tannewt commented 5 years ago

I usually use gdb. You should be able to use it if you can burn bootloaders over SWD. There are general instructions here: https://learn.adafruit.com/debugging-the-samd21-with-gdb

wallarug commented 5 years ago

Hi @tannewt ,

I found a way. It's not clean but confirms values in a similar way to that code you linked to.

Line ~60, main.c

// additional for SPI hack
#include "supervisor/spi_flash_api.h"
#include <stdio.h>

Line ~256, main.c

print_safe_mode_message(safe_mode);
            serial_write("\n");
            serial_write_compressed(translate("Press any key to enter the REPL. Use CTRL-D to reload."));

        /// @wallarug added this very dodgy hack
            serial_write("\n");
        serial_write_compressed(translate("** Checking SPI Flash Device. **"));
        serial_write("\n");

        uint8_t jedec_id_response[3] = {0xff, 0xff, 0xff}; 
            while (jedec_id_response[0] == 0xff) {
            spi_flash_read_command(0x9f, jedec_id_response, 3);
            }

       if(jedec_id_response[0] == 0xbf){
        serial_write_compressed(translate("id: correct"));
       }
       if(jedec_id_response[1] == 0x26){
        serial_write_compressed(translate("type: correct"));
       }
       if(jedec_id_response[2] == 0x41){
        serial_write_compressed(translate("capacity: correct"));
       }

So the chip is reporting the following values as 'correct':

manufacturer_id: 0xbf memory_type: 0x26 capacity: 0x41

I guess now it is just down to working out the other functionality of the flash chip, then it can be known for the future and others.

I shall report back later.

Thank you for all the support.

wallarug commented 5 years ago

I did not have any luck changing the settings of the flash chip to get the CIRCUITPY drive to show up.

I tried a number of different configurations based on the datasheet, however, none worked.

#define ST26VF016B {\
    .total_size = (1 << 21), /* 2 MiB */ \
    .start_up_time_us = 300, \
    .manufacturer_id = 0xbf, \
    .memory_type = 0x26, \
    .capacity = 0x41, \
    .max_clock_speed_mhz = 104, \
    .quad_enable_bit_mask = 0x00, \
    .has_sector_protection = false, \
    .supports_fast_read = true, \
    .supports_qspi = true, \
    .supports_qspi_writes = true, \
    .write_status_register_split = false, \
    .single_status_byte = true, \
}

I think I might just use a different flash chip.

tannewt commented 5 years ago

@wallarug Any update on this?

wallarug commented 5 years ago

Hey Scott,

I gave up on this particular flash chip. The manufacturer could not provide the correct timings to make it work with CircuitPython either.

Maybe if I have a lifetime of spare time I could fix it. But for now, this can be closed as a won't fix.