fpga_config: Check SPI Flash communication - Check the SPI Flash chip is readable after the FPGA is configured over serial. - This fixes SPI flash communication assuming ./out/gn/obj/fpga/toplevel/toplevel.bin is used to configure the FPGA. As of https://pigweed-review.googlesource.com/c/gonk/+/185890 the SPI bus pins are set to inputs on the FPGA side which allows MCU to SPI flash chip communication. - Small refactor removing global Arduino `SPI` usage in spi_flash.cc. - Add ResumeFromPowerDown() to SpiFlash class. Change-Id: Ibb3a32f3e762c6402f3ed0e4e07e639334e17e2a Reviewed-on: https://pigweed-review.googlesource.com/c/gonk/+/185870 Reviewed-by: Eric Holland <hollande@google.com> Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com> Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
diff --git a/README.md b/README.md index 3e166f1..8094b68 100644 --- a/README.md +++ b/README.md
@@ -176,19 +176,19 @@ ## Misc | Net | STM32 Pin | STM32 Function | Function | -|--------+-----------+----------------+----------| +|--------|-----------|----------------|----------| | STATUS | PB13 | GPIO_Output | STAT LED | ## SPI Flash Connection -| Net | STM32 Pin | STM32 Function | Flash Pin | -|--------------|-----------|----------------|------------| -| ICE_SPI_SS | PD2 | GPIO_Output | S# | -| ICE_SPI_MISO | PB4 | SPI1_MISO | DQ1 | -| ICE_SPI_MOSI | PB5 | SPI1_MOSI | DQ0 | -| ICE_SPI_SCK | PB3 | SPI1_SCK | C | -| FLASH_HOLD | PC11 | GPIO_Output | HOLD#/DQ3 | -| FLASH_WP | PC12 | GPIO_Output | W#/VPP/DQ2 | +| Net | FPGA IO# | STM32 Pin | STM32 Function | Flash Pin | +|--------------|----------|-----------|----------------|------------| +| ICE_SPI_SS | 71 | PD2 | GPIO_Output | S# | +| ICE_SPI_MISO | 68 | PB4 | SPI1_MISO | DQ1 | +| ICE_SPI_MOSI | 67 | PB5 | SPI1_MOSI | DQ0 | +| ICE_SPI_SCK | 70 | PB3 | SPI1_SCK | C | +| FLASH_HOLD | 63 | PC11 | GPIO_Output | HOLD#/DQ3 | +| FLASH_WP | 64 | PC12 | GPIO_Output | W#/VPP/DQ2 | ## FPGA Connection
diff --git a/applications/fpga_config/main.cc b/applications/fpga_config/main.cc index d028721..a2828a9 100644 --- a/applications/fpga_config/main.cc +++ b/applications/fpga_config/main.cc
@@ -37,7 +37,8 @@ PinConfig pin_config = PinConfig(); FpgaControl fpga_control = FpgaControl(&pin_config); -SpiFlash spi_flash = SpiFlash(FlashCS, /*baudrate=*/1000000); +SpiFlash spi_flash = + SpiFlash(FlashCS, /*baudrate=*/1000000, pin_config.flash_spi); void ice40_done_rising_isr() { PW_LOG_DEBUG("ICE40 Done: HIGH"); } @@ -81,10 +82,24 @@ } } +void CheckSpiFlash() { + PW_LOG_INFO("Checking SPI Flash"); + spi_flash.StartFlashSpi(); + + pw::Result<SpiFlash::DeviceId> result = spi_flash.GetDeviceIds(); + if (result.ok()) { + PW_LOG_INFO("SPI Flash JEDEC ID: %x %x %x", result.value().manufacturer_id, + result.value().family_code, result.value().product_version); + } +} + void FpgaConfigTask() { auto result = fpga_control.StartConfig(); if (result.ok()) { + // FPGA has been configure successfully. current_task = &IdleTask; + // Check if SPI flash is readable. + CheckSpiFlash(); } else { PW_LOG_INFO("Restarting FPGA Config."); current_task = &FpgaConfigTask;
diff --git a/applications/spi_flash_test/main.cc b/applications/spi_flash_test/main.cc index daaaa04..70463be 100644 --- a/applications/spi_flash_test/main.cc +++ b/applications/spi_flash_test/main.cc
@@ -33,7 +33,8 @@ namespace { PinConfig pin_config = PinConfig(); -SpiFlash spi_flash = SpiFlash(FlashCS, /*baudrate=*/1000000); +SpiFlash spi_flash = + SpiFlash(FlashCS, /*baudrate=*/1000000, pin_config.flash_spi); } // namespace @@ -46,6 +47,7 @@ delay(10); pin_config.SPIEnable(); + spi_flash.StartFlashSpi(); char buffer[32]; uint16_t update_count = 0;
diff --git a/lib/fpga_control/fpga_control.cc b/lib/fpga_control/fpga_control.cc index 7d4ff6a..090ad54 100644 --- a/lib/fpga_control/fpga_control.cc +++ b/lib/fpga_control/fpga_control.cc
@@ -43,10 +43,12 @@ uint32_t last_update = millis(); uint32_t this_update = millis(); + const uint32_t update_interval_ms = 1000; + while (true) { // Output a heartbeat message while waiting for data. this_update = millis(); - if (bytes_written == 0 && this_update > last_update + 1000) { + if (bytes_written == 0 && this_update > last_update + update_interval_ms) { last_update = this_update; PW_LOG_INFO("Waiting for bitstream"); } @@ -112,7 +114,7 @@ return pw::OkStatus(); } -Status FpgaControl::SendBitstreamToFpga() { +Status FpgaControl::SendBitstreamToFpga(const uint32_t config_timeout_ms) { PW_LOG_INFO("Sending bitstream file to the FPGA."); pin_config_->SPIDisable(); @@ -163,7 +165,7 @@ this_update = millis(); // If more than two seconds have passed something likely went wrong. - if (this_update > last_update + 2000) { + if (this_update > last_update + config_timeout_ms) { PW_LOG_ERROR("FPGA Config failed."); last_update = this_update; return pw::Status::Aborted(); @@ -173,11 +175,14 @@ // Send 50 more clock pulses to release release the SPI bus to user control. for (int i = 0; i < 50; i++) { digitalWrite(FlashCLK, LOW); - delayMicroseconds(200); + delayMicroseconds(100); digitalWrite(FlashCLK, HIGH); - delayMicroseconds(200); + delayMicroseconds(100); } + // Re-enable SPI + pin_config_->SPIEnable(); + return pw::OkStatus(); }
diff --git a/lib/fpga_control/public/gonk/fpga_control.h b/lib/fpga_control/public/gonk/fpga_control.h index 3aa35f6..7493da9 100644 --- a/lib/fpga_control/public/gonk/fpga_control.h +++ b/lib/fpga_control/public/gonk/fpga_control.h
@@ -24,7 +24,7 @@ Status WaitForBitstreamStart(); Status ReadBitstream(); Status VerifyBitstream(); - Status SendBitstreamToFpga(); + Status SendBitstreamToFpga(const uint32_t config_timeout_ms = 2000); private: PinConfig *pin_config_;
diff --git a/lib/pin_config/BUILD.gn b/lib/pin_config/BUILD.gn index ef9b399..42cbc01 100644 --- a/lib/pin_config/BUILD.gn +++ b/lib/pin_config/BUILD.gn
@@ -17,15 +17,16 @@ import("$dir_pw_build/target_types.gni") config("default_config") { - include_dirs = [ "public" ] + include_dirs = [ + "public", + "//third_party/stm32duino/arduino-core/libraries/SPI/src", + ] } pw_source_set("pin_config") { public_configs = [ ":default_config" ] public = [ "public/gonk/pin_config.h" ] - include_dirs = [ "//third_party/stm32duino/arduino-core/libraries/SPI/src" ] - sources = [ "pin_config.cc" ] deps = [
diff --git a/lib/pin_config/pin_config.cc b/lib/pin_config/pin_config.cc index 5489b52..9debc76 100644 --- a/lib/pin_config/pin_config.cc +++ b/lib/pin_config/pin_config.cc
@@ -10,7 +10,7 @@ namespace gonk::pin_config { -PinConfig::PinConfig() {} +PinConfig::PinConfig() { flash_spi = SPIClass(FlashMOSI, FlashMISO, FlashCLK); } void PinConfig::Init() { // Set STATUS LED mode and initial state. @@ -87,24 +87,14 @@ void PinConfig::FpgaEnable() { digitalWrite(ICE40ResetN, HIGH); } void PinConfig::SPIEnable() { - // SPI pin definitions - SPI.setMISO(FlashMISO); - SPI.setMOSI(FlashMOSI); - SPI.setSCLK(FlashCLK); - SPI.begin(); - - // Set FlashCS as output with initial state. - pinMode(FlashCS, OUTPUT); - digitalWrite(FlashCS, HIGH); - - pinMode(FlashCLK, OUTPUT); - pinMode(FlashMOSI, OUTPUT); - pinMode(FlashMISO, INPUT_FLOATING); + // Disable Flash hold and write protect. + pinMode(FlashHold, OUTPUT); + pinMode(FlashWP, OUTPUT); + digitalWrite(FlashHold, HIGH); + digitalWrite(FlashWP, HIGH); } void PinConfig::SPIDisable() { - SPI.end(); - pinMode(FlashCS, OUTPUT); pinMode(FlashCLK, OUTPUT); pinMode(FlashMOSI, OUTPUT);
diff --git a/lib/pin_config/public/gonk/pin_config.h b/lib/pin_config/public/gonk/pin_config.h index 06c541f..c3488ec 100644 --- a/lib/pin_config/public/gonk/pin_config.h +++ b/lib/pin_config/public/gonk/pin_config.h
@@ -1,6 +1,7 @@ #pragma once #include <Arduino.h> +#include <SPI.h> namespace gonk::pin_config { @@ -32,6 +33,8 @@ void SPIEnable(); void SPIDisable(); + SPIClass flash_spi; + private: };
diff --git a/lib/spi_flash/public/gonk/spi_flash.h b/lib/spi_flash/public/gonk/spi_flash.h index 86a0fc5..f84d842 100644 --- a/lib/spi_flash/public/gonk/spi_flash.h +++ b/lib/spi_flash/public/gonk/spi_flash.h
@@ -20,7 +20,7 @@ uint8_t product_version; }; - SpiFlash(uint16_t flash_cs, int baudrate); + SpiFlash(uint16_t flash_cs, int baudrate, SPIClass &flash_spi); Status WriteEnable(); Status WriteDisable(); @@ -29,10 +29,14 @@ bool IsBusy(); bool WritingIsEnabled(); Status Erase(); + void StartFlashSpi(); + void StopFlashSpi(); + void ResumeFromPowerDown(); private: uint16_t flash_cs_; SPISettings spi_settings_; + SPIClass &flash_spi_; }; } // namespace gonk::spi_flash
diff --git a/lib/spi_flash/spi_flash.cc b/lib/spi_flash/spi_flash.cc index d518211..9202ed1 100644 --- a/lib/spi_flash/spi_flash.cc +++ b/lib/spi_flash/spi_flash.cc
@@ -16,25 +16,26 @@ namespace gonk::spi_flash { -SpiFlash::SpiFlash(uint16_t flash_cs, int baudrate) - : flash_cs_(flash_cs), spi_settings_(baudrate, MSBFIRST, SPI_MODE0) {} +SpiFlash::SpiFlash(uint16_t flash_cs, int baudrate, SPIClass &flash_spi) + : flash_cs_(flash_cs), spi_settings_(baudrate, MSBFIRST, SPI_MODE0), + flash_spi_(flash_spi) {} Status SpiFlash::WriteEnable() { - SPI.beginTransaction(spi_settings_); + flash_spi_.beginTransaction(spi_settings_); digitalWrite(flash_cs_, LOW); - SPI.transfer(0x06); + flash_spi_.transfer(0x06); digitalWrite(flash_cs_, HIGH); - SPI.endTransaction(); + flash_spi_.endTransaction(); return pw::OkStatus(); } Status SpiFlash::WriteDisable() { - SPI.beginTransaction(spi_settings_); + flash_spi_.beginTransaction(spi_settings_); digitalWrite(flash_cs_, LOW); - SPI.transfer(0x04); + flash_spi_.transfer(0x04); digitalWrite(flash_cs_, HIGH); - SPI.endTransaction(); + flash_spi_.endTransaction(); return pw::OkStatus(); } @@ -45,11 +46,11 @@ 0, // Result value }; - SPI.beginTransaction(spi_settings_); + flash_spi_.beginTransaction(spi_settings_); digitalWrite(flash_cs_, LOW); - SPI.transfer(status_register, 2); + flash_spi_.transfer(status_register, 2); digitalWrite(flash_cs_, HIGH); - SPI.endTransaction(); + flash_spi_.endTransaction(); return (std::bitset<8>)status_register[1]; } @@ -71,11 +72,11 @@ PW_LOG_DEBUG("Start Flash Erase"); // Begin chip erase. - SPI.beginTransaction(spi_settings_); + flash_spi_.beginTransaction(spi_settings_); digitalWrite(flash_cs_, LOW); - SPI.transfer(0x60); + flash_spi_.transfer(0x60); digitalWrite(flash_cs_, HIGH); - SPI.endTransaction(); + flash_spi_.endTransaction(); int wait_time = 100; // Wait at most 1 second. while (IsBusy() && wait_time > 0) { @@ -98,11 +99,11 @@ }; // Read the SPI Flash JEDEC ID. - SPI.beginTransaction(spi_settings_); + flash_spi_.beginTransaction(spi_settings_); digitalWrite(flash_cs_, LOW); - SPI.transfer(manufacturer_id, 4); + flash_spi_.transfer(manufacturer_id, 4); digitalWrite(flash_cs_, HIGH); - SPI.endTransaction(); + flash_spi_.endTransaction(); PW_LOG_DEBUG("JEDEC ID: %x %x %x", manufacturer_id[1], manufacturer_id[2], manufacturer_id[3]); @@ -122,4 +123,21 @@ }; } +void SpiFlash::ResumeFromPowerDown() { + // Resume from Deep Power-Down + flash_spi_.beginTransaction(spi_settings_); + digitalWrite(flash_cs_, LOW); + flash_spi_.transfer(0xAB); + digitalWrite(flash_cs_, HIGH); + flash_spi_.endTransaction(); +} + +void SpiFlash::StartFlashSpi() { + flash_spi_.begin(); + delay(10); + ResumeFromPowerDown(); +} + +void SpiFlash::StopFlashSpi() { flash_spi_.end(); } + } // namespace gonk::spi_flash