Trying to get QSPI Memory working

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I've been at this for more than one day now, I have a SAMD51J19 chip and on the board is a QSPI Flash Memory. I've pulled up the data sheet for it and it supports a "fast read" at 120MHz with one dummy cycle.

 

So I programmed it in the instruction frame but when I go to read the QSPI area, nothing happens, it just reads 0's however there is suppose to be information on the flash memory already so maybe I'm doing this wrong.

 

Can someone look over my code below to help me figure this out, I'm sure it's probably a stupid mistake I'm making because I'm new to all of this or maybe there really is nothing on the chip but I need to first ensure the code below looks correct and that I'm doing it right?

 

void setup_qspiOut(var8 port, var8 pin)
{
  PORT->Group[port].DIRSET.bit.DIRSET = (1 << pin);
  PORT->Group[port].OUTCLR.bit.OUTCLR = (1 << pin);
  PORT->Group[port].PINCFG[pin].reg = PORT_PINCFG_PMUXEN;

  if(pin % 2 == 0)
    PORT->Group[port].PMUX[pin / 2].bit.PMUXE = 7;
  else
    PORT->Group[port].PMUX[pin / 2].bit.PMUXO = 7;
}

void setup_qspiIn(var8 port, var8 pin)
{
  PORT->Group[port].CTRL.bit.SAMPLING = (1 << pin);
  PORT->Group[port].DIRCLR.bit.DIRCLR = (1 << pin);
  PORT->Group[port].OUTCLR.bit.OUTCLR = (1 << pin);
  PORT->Group[port].PINCFG[pin].reg = PORT_PINCFG_PMUXEN | PORT_PINCFG_INEN;

  if(pin % 2 == 0)
    PORT->Group[port].PMUX[pin / 2].bit.PMUXE = 7;
  else
    PORT->Group[port].PMUX[pin / 2].bit.PMUXO = 7;
}

int main(void)
{
  // Boot device
  Boot::boot();

  // Setup QSPI Pins
  /*
   MOSI   PA08	Out
   MISO   PA09	In
   WP     PA10	Out
   HOLD   PA11	Out
   SCK    PB10	Out
   CS     PB11	Out
  */
  setup_qspiOut(0, 8);
  setup_qspiIn(0, 9);
  setup_qspiOut(0, 10);
  setup_qspiOut(0, 11);
  setup_qspiOut(1, 10);
  setup_qspiOut(1, 11);

  // De-assert chip mode after each transfer, QSPI is dealing with a memory device
  QSPI->CTRLB.reg = QSPI_CTRLB_CSMODE_SYSTEMATICALLY | QSPI_CTRLB_MODE_MEMORY;

  // Output is on rising edge and Input is on falling edge
  QSPI->BAUD.reg = QSPI_BAUD_CPHA;

  // Give instruction for high-speed read
  QSPI->INSTRCTRL.bit.INSTR = 0x0B;

  QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_DUMMYLEN(1) | QSPI_INSTRFRAME_DDREN | QSPI_INSTRFRAME_TFRTYPE_READMEMORY | QSPI_INSTRFRAME_DATAEN | QSPI_INSTRFRAME_ADDREN | QSPI_INSTRFRAME_INSTREN;

  // Enable QSPI
  QSPI->CTRLA.bit.ENABLE = true;

  var32 tmp = get32(0x04000000);
}

 

This topic has a solution.
Last Edited: Sun. Sep 29, 2019 - 03:42 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Never-mind I think I'm figuring it out. Got it working to some extent although still can't do fast reads, it for some reason works with slow reads.

 

Also I'm still not clear on the whole QSPI data area, does it do a memory-wide read or does it only read the 32-bit value I request at a specific area. In the debugger the entire area is populated with 0xFFFFFFFF but I'm not sure if it's populating the entire area with the value of a single 32-bit value or if what I'm seeing actually is all the different bytes of the flash memory.

 

The QSPI is confusing and I hate that's theirs really no help anywhere in the world except what's in the one data sheet.

 

#include "QspiMemory.h"

#include "sam.h"
#include "../common/data.h"

void QspiMemory::boot()
{
  // Setup QSPI Pins
  /*
   MOSI   PA08	Out
   MISO   PA09	In
   WP     PA10	Out
   HOLD   PA11	Out
   SCK    PB10	Out
   CS     PB11	Out
  */
  _setup_pin_in(0, 8); // Along with PMUX this allows bi-directional io
  _setup_pin_in(0, 9); // Along with PMUX this allows bi-directional io
  _setup_pin_in(0, 10); // Along with PMUX this allows bi-directional io
  _setup_pin_in(0, 11); // Along with PMUX this allows bi-directional io
  _setup_pin_out(1, 10);
  _setup_pin_out(1, 11);

  // De-assert chip mode after each transfer, QSPI is dealing with a memory device
  QSPI->CTRLB.reg = QSPI_CTRLB_CSMODE_SYSTEMATICALLY | QSPI_CTRLB_MODE_MEMORY;

  // Output is on rising edge and Input is on falling edge
  QSPI->BAUD.reg = QSPI_BAUD_CPHA | QSPI_BAUD_BAUD(1);

  // Enable both interrupt flags to be set
  QSPI->INTENSET.bit.INSTREND = true;
  QSPI->INTENSET.bit.CSRISE = true;

  // Enable QSPI
  QSPI->CTRLA.bit.ENABLE = true;

  // Do a read to populate QSPI memory area
  read();
}

void QspiMemory::issueCmd(var8 cmd)
{
  QSPI->INSTRCTRL.bit.INSTR = cmd;
  QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_TFRTYPE_WRITE | QSPI_INSTRFRAME_INSTREN;
  poll_ready();
}

void QspiMemory::read()
{
  QSPI->INSTRCTRL.bit.INSTR = 0x03;

  QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_DUMMYLEN(0) | QSPI_INSTRFRAME_TFRTYPE_READMEMORY | QSPI_INSTRFRAME_DATAEN | QSPI_INSTRFRAME_ADDREN | QSPI_INSTRFRAME_INSTREN;

  // Dummy read
  var32 tmp = QSPI->INSTRFRAME.reg;

  // Actual Read
  tmp = get32(0x04000000);

  // Announce last byte
  QSPI->CTRLA.bit.LASTXFER = true;

  poll_ready();
}

void QspiMemory::poll_ready()
{
  while(QSPI->INTFLAG.bit.INSTREND == 0 && QSPI->INTFLAG.bit.CSRISE == 0);
  QSPI->INTFLAG.bit.INSTREND = true;
  QSPI->INTFLAG.bit.CSRISE = true;
}

void QspiMemory::_setup_pin_out(var8 port, var8 pin)
{
  PORT->Group[port].DIRSET.bit.DIRSET = (1 << pin);
  PORT->Group[port].OUTCLR.bit.OUTCLR = (1 << pin);
  PORT->Group[port].PINCFG[pin].reg = PORT_PINCFG_PMUXEN;

  if(pin % 2 == 0)
    PORT->Group[port].PMUX[pin / 2].bit.PMUXE = 7;
  else
    PORT->Group[port].PMUX[pin / 2].bit.PMUXO = 7;
}

void QspiMemory::_setup_pin_in(var8 port, var8 pin)
{
  PORT->Group[port].CTRL.bit.SAMPLING = (1 << pin);
  PORT->Group[port].DIRCLR.bit.DIRCLR = (1 << pin);
  PORT->Group[port].OUTCLR.bit.OUTCLR = (1 << pin);
  PORT->Group[port].PINCFG[pin].reg = PORT_PINCFG_PMUXEN | PORT_PINCFG_INEN;

  if(pin % 2 == 0)
    PORT->Group[port].PMUX[pin / 2].bit.PMUXE = 7;
  else
    PORT->Group[port].PMUX[pin / 2].bit.PMUXO = 7;
}

 

EDIT

 

Nope, I'm totally lost. I have no idea what I'm doing. even the status registers always read 0xFF with a BAUD rate of 1, or sometimes 0x00 or 0x01 with different baud rate or settings.

 

EDIT 2:

 

I found a rough solution.

 

1. I had the shift in and out flags mixed up for data in and out

2. I didn't understand well the instruction frame so I was getting a lot of stuff mixed up and incorrect. The manual is pretty vague after all. But this is my latest attempt and I think it might be either correct or on a better path.

 

var32 QspiMemory::tmp = 0;

void QspiMemory::boot()
{
  // Setup QSPI Pins
  /*
   MOSI   PA08	Out
   MISO   PA09	In
   WP     PA10	Out
   HOLD   PA11	Out
   SCK    PB10	Out
   CS     PB11	Out
  */
  _setup_pin_in(0, 8); // Along with PMUX this allows bi-directional io
  _setup_pin_in(0, 9); // Along with PMUX this allows bi-directional io
  _setup_pin_in(0, 10); // Along with PMUX this allows bi-directional io
  _setup_pin_in(0, 11); // Along with PMUX this allows bi-directional io
  _setup_pin_out(1, 10);
  _setup_pin_out(1, 11);

  // De-assert chip mode after each transfer, QSPI is dealing with a memory device
  QSPI->CTRLB.reg = QSPI_CTRLB_CSMODE_SYSTEMATICALLY | QSPI_CTRLB_MODE_MEMORY;

  // Output is on rising edge and Input is on falling edge
  QSPI->BAUD.reg = QSPI_BAUD_BAUD(1);

  // Enable both interrupt flags to be set
  QSPI->INTENSET.bit.INSTREND = true;
  QSPI->INTENSET.bit.CSRISE = true;

  // Enable QSPI
  QSPI->CTRLA.bit.ENABLE = true;

  // Load up a read instruction by default
  // This opens up the QSPI memory for reading and XIP
  read_instr();
}

void QspiMemory::cmd(var8 cmd)
{
  new_instr();
  QSPI->INSTRCTRL.bit.INSTR = cmd;
  QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_TFRTYPE_WRITE | QSPI_INSTRFRAME_INSTREN;
}

void QspiMemory::read_instr()
{
  new_instr();
  QSPI->INSTRCTRL.bit.INSTR = 0x03;
  QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_DUMMYLEN(0) | QSPI_INSTRFRAME_TFRTYPE_READMEMORY | QSPI_INSTRFRAME_DATAEN | QSPI_INSTRFRAME_ADDREN | QSPI_INSTRFRAME_INSTREN;
  _read_sync_instr();
}

void QspiMemory::read_status_low_instr()
{
  new_instr();
  QSPI->INSTRCTRL.bit.INSTR = 0x05;
  QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_TFRTYPE_READ | QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_DATAEN;
  _read_sync_instr();
}

void QspiMemory::read_status_high_instr()
{
  new_instr();
  QSPI->INSTRCTRL.bit.INSTR = 0x35;
  QSPI->INSTRFRAME.reg = QSPI_INSTRFRAME_TFRTYPE_READ | QSPI_INSTRFRAME_INSTREN | QSPI_INSTRFRAME_DATAEN;
  _read_sync_instr();
}

// Changes mode over to a new instruction if there's a current open one
void QspiMemory::new_instr()
{
  // If the chip select line is not up
  // do nothing
  if(QSPI->STATUS.bit.CSSTATUS)
  return;

  // Mark end of instruction
  QSPI->CTRLA.bit.LASTXFER = true;

  // Wait for it to end
  while(QSPI->INTFLAG.bit.INSTREND == 0 && QSPI->INTFLAG.bit.CSRISE == 0);

  // Clear out flags
  QSPI->INTFLAG.bit.INSTREND = true;
  QSPI->INTFLAG.bit.CSRISE = true;
}

void QspiMemory::_setup_pin_out(var8 port, var8 pin)
{
  PORT->Group[port].DIRSET.bit.DIRSET = (1 << pin);
  PORT->Group[port].OUTCLR.bit.OUTCLR = (1 << pin);
  PORT->Group[port].PINCFG[pin].reg = PORT_PINCFG_PMUXEN;

  if(pin % 2 == 0)
    PORT->Group[port].PMUX[pin / 2].bit.PMUXE = 7;
  else
    PORT->Group[port].PMUX[pin / 2].bit.PMUXO = 7;
}

void QspiMemory::_setup_pin_in(var8 port, var8 pin)
{
  PORT->Group[port].CTRL.bit.SAMPLING = (1 << pin);
  PORT->Group[port].DIRCLR.bit.DIRCLR = (1 << pin);
  PORT->Group[port].OUTCLR.bit.OUTCLR = (1 << pin);
  PORT->Group[port].PINCFG[pin].reg = PORT_PINCFG_PMUXEN | PORT_PINCFG_INEN;

  if(pin % 2 == 0)
    PORT->Group[port].PMUX[pin / 2].bit.PMUXE = 7;
  else
    PORT->Group[port].PMUX[pin / 2].bit.PMUXO = 7;
}

void QspiMemory::_read_sync_instr()
{
  tmp = QSPI->INSTRFRAME.reg;
}

 

Last Edited: Sun. Sep 29, 2019 - 03:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0