Program Listing for File dm_fx_dsp.cpp

Return to documentation for file (src/dm_fx_dsp.cpp)

// Copyright (c) 2020 Run Jump Labs LLC.  All right reserved.
// This code is licensed under MIT license (see license.txt for details)

#include <SPI.h>

#include "dreammakerfx.h"
#include "dm_fx_dsp.h"


#ifndef DOXYGEN_SHOULD_SKIP_THIS


bool  wait_for_dsp_spi_flash_access_to_cease(void) {

  // Wait for SPI flash select line to stop strobing
  pinMode(PIN_DSP_SPI_FLASH_SELECT, INPUT);
  uint32_t start = millis();
  uint32_t now = millis();
  uint32_t cntr = 0;
  uint32_t iters = 0;
  while (cntr < 250 && millis() < start + 3000) {
    if (digitalRead(PIN_DSP_SPI_FLASH_SELECT) == LOW) {
      iters++;
      now = millis();
    }
    cntr = millis() - now;
  }
  //Serial.println(iters);
  if (millis() > start + 3000) {
    DEBUG_MSG("DSP boot timeout", MSG_ERROR);
    return false;
  }

  return true;

}


bool  dsp_reset(void) {

  // Set reset pin to output
  pinMode(PIN_DSP_RESET, OUTPUT);
  digitalWrite(PIN_DSP_RESET, HIGH);

  // Flush existing data from SPI fifo
  dsp_status.firmware_valid = false;
  dsp_status.firmware_ver = 0;
  spi_fifo_reset();

  // Turn off SPI so there is no contention on the boot process
  spi_stop();

  // Strobe reset
  delay(50);
  digitalWrite(PIN_DSP_RESET, LOW);
  delay(100);
  digitalWrite(PIN_DSP_RESET, HIGH);
  delay(20);


  return wait_for_dsp_spi_flash_access_to_cease();

}


bool wait_for_canvas_to_start(void) {

  DEBUG_MSG("Starting", MSG_DEBUG);

  dsp_status.state_canvas_running = false;
  int delay = 25;
  int timeout_cntr_1s = 1000/delay;

  while (!dsp_status.state_canvas_running && timeout_cntr_1s) {
    spi_fifo_push_emptry_frame();
    spi_transmit_buffered_frames(false);
    int now = millis();
    while (millis() < now + delay) {
      display_data_from_sharc();
    }
    timeout_cntr_1s--;
  }
  if (!timeout_cntr_1s) {
    DEBUG_MSG("Canvas never started running", MSG_ERROR);
    display_error_status(ERROR_CODE_DSP_NOT_BOOTING);
  }

  DEBUG_MSG("Complete", MSG_DEBUG);

  return dsp_status.state_canvas_running;
}





bool wait_for_dsp_firmware(void) {


  dsp_status.firmware_valid = false;
  int delay_time = 25;
  int timeout_cntr_1s = 5000/delay_time;

  while (!dsp_status.firmware_valid && timeout_cntr_1s--) {
    spi_fifo_push_emptry_frame();
    spi_transmit_buffered_frames(false);
    if (!dsp_status.firmware_valid) {
      delay(delay_time);
    }
  }

  if (!dsp_status.firmware_valid) {
    return false;
  } else {
    return true;
  }

}

void  display_dsp_firmware(void) {
  // Display firmwware version
  char msg[64], ver[10];
  String firmware_str = String(dsp_status.firmware_ver);
  firmware_str.replace("0",".");
  firmware_str.toCharArray(ver, sizeof(ver));
  sprintf(msg, "DSP firmware version: %s", ver);
  DEBUG_MSG(msg, MSG_INFO);
}


void  wait_for_dsp_to_boot(void) {

  DEBUG_MSG("Starting", MSG_DEBUG);

  int success_cntr = 5;
  bool booted = false;
  while (!booted && success_cntr) {
    spi_start();
    booted = wait_for_dsp_firmware();
    if (!booted) {
      DEBUG_MSG("DSP did not boot, attempting reset", MSG_WARN);
      spi_stop();
      if (!dmfx_debug_no_reset) {
        dsp_reset();
      } else {
        DEBUG_MSG("DSP does not appear to be running but cannot reset since we're in no reset mode", MSG_ERROR);
        display_error_status(ERROR_CODE_DSP_NOT_BOOTING);
      }
    }
    success_cntr--;
  }

  if (!success_cntr) {
    DEBUG_MSG("DSP was not able to boot", MSG_ERROR);
    display_error_status(ERROR_CODE_DSP_NOT_BOOTING);
  }

  display_dsp_firmware();

  DEBUG_MSG("Complete", MSG_DEBUG);

}

void  wait_for_dsp_to_be_ready(void) {

  DEBUG_MSG("Starting", MSG_DEBUG);

  int delay = 25;


  bool ready = false;
  int success_cntr = 5;
  while (!ready && success_cntr) {
    int timeout_cntr_3s = 3000/delay;
    // wait for audio to start
    while ((dsp_status.state_flags & 0x70) != 0x70 && timeout_cntr_3s) {
      spi_fifo_push_emptry_frame();
      spi_transmit_buffered_frames(false);
      int now = millis();
      while (millis() < now + delay) {
        display_data_from_sharc();
      }
      timeout_cntr_3s--;
    }

    if (timeout_cntr_3s) {
      ready = true;
    } else {
      success_cntr--;
      if (!dmfx_debug_no_reset) {
        DEBUG_MSG("Timeout waiting for DSP to start-up: resetting", MSG_ERROR);
        dsp_reset();
        wait_for_dsp_to_boot();
      }  else {
        DEBUG_MSG("DSP does not appear to be running but cannot reset since we're in no reset mode", MSG_ERROR);
        display_error_status(ERROR_CODE_DSP_NOT_BOOTING);
      }
    }
  }
  DEBUG_MSG("Complete", MSG_DEBUG);

}


void report_canvas_errors(void) {
  if (dsp_status.state_err_allocation) {
    DEBUG_MSG("Allocation error encountered while initializing effects", MSG_ERROR);
    display_error_status(ERROR_INTERNAL);
  }
  if (dsp_status.state_err_param) {
    DEBUG_MSG("Parameter error encountered while initializing effects", MSG_ERROR);
    display_error_status(ERROR_INTERNAL);
  }
  if (dsp_status.state_err_corrupt) {
    DEBUG_MSG("Corruption error encountered while initializing effects", MSG_ERROR);
    display_error_status(ERROR_INTERNAL);
  }
  if (dsp_status.state_err_other) {
    DEBUG_MSG("Other error encountered while initializing effects", MSG_ERROR);
    display_error_status(ERROR_INTERNAL);
  }
}




void display_data_from_sharc(void) {
  static int line_indx = 0;
  static char line[256];

  while (Serial1.available() > 0) {
    char b = Serial1.read();
    if (b == '\n') {
      if (line_indx > 5) {
        line[line_indx] = 0;
        DEBUG_MSG(line, MSG_INFO);
      }
      line_indx = 0;
    } else {
      line[line_indx++] = b;
      if (line_indx >= sizeof(line)) {
        line_indx = sizeof(line) - 1;
      }
    }
  }
}




// Defines for the SPI flash memory
#define CMD_SPI_READ            (0x03)
#define CMD_SPI_PROG_PAGE       (0x02)
#define CMD_SPI_SECTOR_ERASE    (0x20)
#define CMD_SPI_BLOCK_ERASE     (0xD8)
#define CMD_SPI_CHIP_ERASE      (0xC7)
#define CMD_SPI_READ_STATUS     (0x05)
#define CMD_SPI_WRITE_STATUS    (0x05)
#define CMD_SPI_WRITE_EN        (0x06)
#define CMD_SPI_WRITE_EN_NV     (0x50)


#define SPI_STATUS_BUSY         (0x1)
#define SPI_STATUS_WRITE_EN     (0x2)


void  dsp_assert_reset(void) {
  digitalWrite(SPI_SHARC_RESET, LOW);
  delay(1);
}

void  dsp_deassert_reset(void) {
  digitalWrite(SPI_SHARC_RESET, HIGH);
}

static void  spi_flash_start_transfer(void) {

    // SHARC bootloader defaults to LSB first
    SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
    digitalWrite(SPI_SHARC_SELECT, LOW);
}

static void  spi_flash_end_transfer(void) {

    SPI.endTransaction();
    digitalWrite(SPI_SHARC_SELECT, HIGH);
    delay(1);
}


static uint8_t spi_flash_read_status_register() {

    uint8_t result;

    spi_flash_start_transfer();

    SPI.transfer(CMD_SPI_READ_STATUS);
    result = SPI.transfer(0x0);

    spi_flash_end_transfer();

    return result;

}


static void spi_flash_send_byte(uint8_t val) {

    // Send a single byte command
    spi_flash_start_transfer();
    SPI.transfer(val);
    spi_flash_end_transfer();
}

static bool spi_flash_check_busy(void) {

    uint8_t val = spi_flash_read_status_register();

    if (val & SPI_STATUS_BUSY) return true;
    else return false;
}


static void    spi_flash_erase_chip(void) {

    if (Serial && dmfx_debug_mode) {
      Serial.print(" - Firmware update: erasing flash...");
    }
    spi_flash_send_byte(CMD_SPI_WRITE_EN);
    spi_flash_send_byte(CMD_SPI_CHIP_ERASE);
    while (spi_flash_check_busy());

    if (Serial && dmfx_debug_mode) {
      Serial.println(" complete");
    }

}

static void   spi_flash_clear_protect(void) {

    spi_flash_start_transfer();
    SPI.transfer(CMD_SPI_WRITE_EN_NV);
    spi_flash_end_transfer();


    spi_flash_start_transfer();

    SPI.transfer(CMD_SPI_WRITE_STATUS);
    SPI.transfer(0x0);

    spi_flash_end_transfer();

}


static uint8_t flip_bit_order(uint8_t num)
{
    unsigned int reverse_num = 0;
    int i;
    for (i=0; i<8; i++)
    {
        if((num & (1 << i)))
           reverse_num |= 1 << ((8 - 1) - i);
   }
   return reverse_num;
}

static bool    spi_flash_page_write(uint32_t address, const uint8_t * vals, uint16_t count) {

    if (count > 256) {
        return false;
    }

  static int page_led_cntr = 0;
  static int led_cntr = 0;
  if (page_led_cntr++ > 8) {

    if (led_cntr++ & 1) {
      turn_on_right_footsw_led();
      turn_off_left_footsw_led();
    } else {
      turn_on_right_footsw_led();
      turn_off_left_footsw_led();
    }

    page_led_cntr = 0;
  }


    uint8_t spi_block[4] = {CMD_SPI_PROG_PAGE};

    // Load address
    spi_block[1] = (address >> 16) & 0xFF;
    spi_block[2] = (address >> 8) & 0xFF;
    spi_block[3] = (address >> 0) & 0xFF;


    spi_flash_send_byte(CMD_SPI_WRITE_EN);

    spi_flash_start_transfer();
    for (int i=0;i<4;i++) {
       SPI.transfer(spi_block[i]);
    }
    for (int i=0;i<count;i++) {
      SPI.transfer(flip_bit_order(vals[i]));
    }
    spi_flash_end_transfer();

    // 3. Wait for transaction to complete
    while (spi_flash_check_busy());

    return true;
}


const PROGMEM uint8_t firmware_image[] = {
    #include "dm_fx_dsp_firmware_image.dat"
};

bool dsp_update_firmware_image(void) {

  uint8_t * vals = (uint8_t *) firmware_image;
  uint32_t count = sizeof(firmware_image);

  // Set up SPI select pin
  pinMode(SPI_SHARC_SELECT, OUTPUT);
  digitalWrite(SPI_SHARC_SELECT, HIGH);

  pinMode(SPI_SHARC_RESET, OUTPUT);
  digitalWrite(SPI_SHARC_RESET, HIGH);


  // Start SPI port
  SPI.begin();

  dsp_assert_reset();

  spi_flash_clear_protect();
  spi_flash_erase_chip();

  uint32_t page_count = (count >> 8) + 1;
  uint32_t address = 0;

  if (Serial && dmfx_debug_mode) {
    Serial.print(" - Firmware update: programming...");
  }

  turn_on_right_footsw_led();
  turn_off_left_footsw_led();

  uint32_t byte_cnt = 0;
  for (int i=0;i<page_count;i++) {
    spi_flash_page_write(address, &vals[address], 256);
    address += 256;
  }

  turn_on_right_footsw_led();
  turn_on_left_footsw_led();

  // Turn SPI select back into input so SHARC can boot
  pinMode(SPI_SHARC_SELECT, INPUT);
  SPI.end();


    if (Serial && dmfx_debug_mode) {
      Serial.println(" complete");
    }


  turn_off_right_footsw_led();
  turn_off_left_footsw_led();

  dsp_deassert_reset();

  return true;

}

#endif