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