Skip to main content
Graduate II
August 13, 2024
Solved

OCTOSPI interface for communication with a FPGA

  • August 13, 2024
  • 5 replies
  • 3113 views

Hi All
I have created a PCB where I want to use the OCTOSPI to communicate with a FPGA.
I have difficulty finding accurate information on how the different memory types work. I already have a Hyperram running so I started there, that did also seem straight forward.
I do currently only have FPGA code listening and is focusing on getting write to work.

I slowed down the clk quite a bit to only fucus on functionality. Pre scale of 30 ~9.6Mhz
I would much like to have the FPGA memory mapped, so that's what I'm testing.

When I write to address 0x91234568 (0x90000000) is octospi1 base
with the data of 0x87654321 then i get following sequence in hex:
20 12 34 56 00 04 43 21 87 65
The Data is easy, that's just different order 43 21 87 65 instead of 87 65 43 21
The 20 seems to be CMD

The Address (in correct order) 12 34 56 (but then always 00 00 or 00 04) as the last, I seem to lack the last nibble of the Address.  The RWDS Seems a bit early.

Kvang_2-1723555880449.png

if I Change to 0x91234560  then  the same 20 12 34 56 00 00 43 21 87 65, just with out the 04

Kvang_1-1723555699939.png

If I write at 0x91234564 I get 4 extra bytes before RWDS.

20 12 34 56 00 00 00 00 00 00 43 21 87 65, But now the RWDS is accurate

Kvang_0-1723555242998.png

If I write at 0x9123456C I also get the 4 extra bytes before RWDS, now with 04.

20 12 34 56 00 04 00 00 00 00 43 21 87 65, and the RWDS is accurate

If I write at 0x91234566 I also get the 6 extra bytes before RWDS, now with 00.

if I write on uneven addresses then bytes a masked with RWDS.

There seems to be a quite clear pattern, that I first discovered while writing this message, but again, it is a bit difficult.

  • Where can I fin accurate information on how this works?
  • Are there a better memory type to chose instead of HYPERBUS. 
  • e.g. APMEMORY That I not yest has got to work in memory mapped mode

My config currently looks like this:

static void MX_OCTOSPI1_Init(void)

{

 

/* USER CODE BEGIN OCTOSPI1_Init 0 */

 

/* USER CODE END OCTOSPI1_Init 0 */

 

OSPIM_CfgTypeDef sOspiManagerCfg = {0};

OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};

 

/* USER CODE BEGIN OCTOSPI1_Init 1 */

 

/* USER CODE END OCTOSPI1_Init 1 */

/* OCTOSPI1 parameter configuration*/

hospi1.Instance = OCTOSPI1;

hospi1.Init.FifoThreshold = 4;

hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;

hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;

hospi1.Init.DeviceSize = 28;

hospi1.Init.ChipSelectHighTime = 8;

hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;

hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;

hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;

hospi1.Init.ClockPrescaler = 30;

hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;

hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;

hospi1.Init.ChipSelectBoundary = 23;

hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;

hospi1.Init.MaxTran = 0;

hospi1.Init.Refresh = 0;

if (HAL_OSPI_Init(&hospi1) != HAL_OK)

{

Error_Handler();

}

sOspiManagerCfg.ClkPort = 1;

sOspiManagerCfg.DQSPort = 1;

sOspiManagerCfg.NCSPort = 1;

sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;

sOspiManagerCfg.IOHighPort = HAL_OSPIM_IOPORT_1_HIGH;

if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

{

Error_Handler();

}

sHyperBusCfg.RWRecoveryTime = 0;

sHyperBusCfg.AccessTime = 0;

sHyperBusCfg.WriteZeroLatency = HAL_OSPI_NO_LATENCY_ON_WRITE;

sHyperBusCfg.LatencyMode = HAL_OSPI_FIXED_LATENCY;

if (HAL_OSPI_HyperbusCfg(&hospi1, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

{

Error_Handler();

}

/* USER CODE BEGIN OCTOSPI1_Init 2 */

 

OSPI_HyperbusCmdTypeDef sCommand;

OSPI_MemoryMappedTypeDef sMemMappedCfg;

 

/* Memory-mapped mode configuration --------------------------------------- */

sCommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE;

sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;

sCommand.DQSMode = HAL_OSPI_DQS_ENABLE; //Test

sCommand.Address = 0;

sCommand.NbData = 1;

 

if (HAL_OSPI_HyperbusCmd(&hospi1, &sCommand, 0) != HAL_OK)

{

Error_Handler();

}

 

sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;

 

if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK)

{

Error_Handler();

}

 

//Enable FREERUNCLK

//SET_BIT(hospi1.Instance->DCR1, OCTOSPI_DCR1_FRCK);

 

/* USER CODE END OCTOSPI1_Init 2 */

 

}

 

Thanks in advance 

Kristian Vang

 

 

 

 

 

    This topic has been closed for replies.
    Best answer by Kvang

    Hi 
    I found that the reason for the last problem was that I was holding the bus from the FPGA, so problem solved, I now have a Stable OctoSpi <> FPGA interface.

    5 replies

    Technical Moderator
    August 13, 2024

    Hello @Kvang ,

     

    Could you provide more details? Which STM32H7 MCU and HyperRAM memory are you using?

    Could you please share the memory datasheet?

    I advise you to take a look at the STM32H7 errata sheet and check the OCTOSPI errata and the workaround.

    If you use the STM32H735 please note that at least six cycles memory latency must be set when DQS is used for HyperBus™ memories (sHyperBusCfg.AccessTime = 0; should be sHyperBusCfg.AccessTime = 6;)
     

    KDJEM1_0-1723560319175.png

     

    May this example "OSPI_HyperRAM_MemoryMapped" can help you. 

    Thank you.

     

    Kaouthar

     

    KvangAuthor
    Graduate II
    August 14, 2024

    Hi  Kaouthar

    Thanks for the quick response.
    I do have a HyperRam ( IS66WVH8M8BLL-100B1LI ) on the Board, but that's not the problem, that works fine runs on OctoSpi2
    Yes I'm using the STM32H735, thanks for the head up on the errata.  
    I want to use the OctoSpi for communication with a FPGA, I will write the VHDL for the interface myself so I'm not bound to any memory interface type, but I want to have the FPGA memory mapped
    will that work with the other types like:
    HAL_OSPI_MEMTYPE_MICRON
    HAL_OSPI_MEMTYPE_MACRONIX
    HAL_OSPI_MEMTYPE_APMEMORY or
    HAL_OSPI_MEMTYPE_MACRONIX_RAM ?
     
    Where do I find information on how-to setup this?
     
    I want the interface to work with least possible latency 
     
    Kristian Vang
    Technical Moderator
    August 14, 2024

    Hello @Kvang ,

    For communication with an FPGA using the OctoSPI interface, you can choose any of the following memory types, depending on your specific requirements:

    • HAL_OSPI_MEMTYPE_MICRON: This mode is typically used for Micron memories (and compatible memories) and follows the D0/D1 ordering in DTR 8-data-bit mode.
    • HAL_OSPI_MEMTYPE_MACRONIX: This mode is used for Macronix memories (and compatible memories) and follows the D1/D0 ordering in DTR 8-data-bit mode.
    • HAL_OSPI_MEMTYPE_APMEMORY: This mode is used for AP Memory devices.
    • HAL_OSPI_MEMTYPE_MACRONIX_RAM: This mode is used for Macronix RAM and includes dedicated address mapping.

    KDJEM1_0-1723621703919.png

    Note that the memory type has no impact in Quad-SPI mode as mentioned in AN5050.

    KDJEM1_1-1723622005989.png

    Thank you.

    Kaouthar

     

    KvangAuthor
    Graduate II
    August 14, 2024

    Hi Kaouthar

    Thanks a lot.

    I got further,  I now have it running in these mode:

    HAL_OSPI_MEMTYPE_MICRON

    HAL_OSPI_MEMTYPE_MACRONIX
    HAL_OSPI_MEMTYPE_APMEMORY or
    HAL_OSPI_MEMTYPE_MACRONIX_RAM
    I used the examples from the  AN5050.
    There does not seem to be any difference, except for data order and address translation, is that correctly understood?
    I assume that since AccessTime is not used for these that the minimum cycle of 6 is not a problem here, right?
     
    There are still a few things that bug me. (do you have more documentation i can get on this, I have looked in the Datasheet and the  AN5050, they are not very specific on how these things work)
     
    I still do not get anything in the last 3 bit of the address, if the start address is not multiple of 8 bytes then the Data is masked with RWDS. I guess that just how it works right?
     
    What Can I expect when I get to implement read? also a minimum of 8 bytes of read? 
     
    The CS topically stays low after transfer, are there anyway to avoid that?
    If I set the hospi1.Init.Refresh to something else but 0 then it goes back high after that time. 
     
    The RWDS also sometimes stay low, Do I need to do anything from the FPGA with the RWDS when the CPU Writing?
     
    I would like to have 24Bit address and 8 bit cmd i only need to signal read / write.
    i set it like that with DTR enabled but it still using 6 bytes. any way to avoid that and just use 4 bytes for CMD and Address?
    If I disable CMD, are there any other way to differ Read from Write?
     
    I noticed that if I use memcpy (in debug i did not try release) then the transfers except the first is divided into weird blocks with RWDS enabling individua bytes. is this expected? I geuss that memcpy transfer byte by byte? i did not investigate. used dma instead, here it works well.
    her is my current init:

    /* OCTOSPI1 parameter configuration*/

    hospi1.Instance = OCTOSPI1;

    hospi1.Init.FifoThreshold = 4;

    hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;

    hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_APMEMORY;

    hospi1.Init.DeviceSize = 24;

    hospi1.Init.ChipSelectHighTime = 1;

    hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;

    hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;

    hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;

    hospi1.Init.ClockPrescaler = 15;

    hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;

    hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;

    hospi1.Init.ChipSelectBoundary = 24;

    hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;

    hospi1.Init.MaxTran = 0;

    hospi1.Init.Refresh = 0;

    if (HAL_OSPI_Init(&hospi1) != HAL_OK)

    {

    Error_Handler();

    }

    sOspiManagerCfg.ClkPort = 1;

    sOspiManagerCfg.DQSPort = 1;

    sOspiManagerCfg.NCSPort = 1;

    sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;

    sOspiManagerCfg.IOHighPort = HAL_OSPIM_IOPORT_1_HIGH;

    if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

    {

    Error_Handler();

    }

    and a function to enable memmory map mode:
     

    /*APS6408L-3OB PSRAM APmemory*/

    #define LINEAR_BURST_READ 0x20

    #define LINEAR_BURST_WRITE 0xA0

    #define DUMMY_CLOCK_CYCLES_SRAM_READ 1

    #define DUMMY_CLOCK_CYCLES_SRAM_WRITE 1


    void EnableMemMapped(void)

    {

    OSPI_RegularCmdTypeDef sCommand;

    OSPI_MemoryMappedTypeDef sMemMappedCfg;

    sCommand.FlashId = HAL_OSPI_FLASH_ID_1;

    sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES;

    sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;

    sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE;

    sCommand.AddressMode = HAL_OSPI_ADDRESS_8_LINES;

    sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;

    sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_ENABLE;

    sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;

    sCommand.DataMode = HAL_OSPI_DATA_8_LINES;

    sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_ENABLE;

    sCommand.DQSMode = HAL_OSPI_DQS_ENABLE;

    sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;

    sCommand.Address = 0;

    sCommand.NbData = 1;

    /* Memory-mapped mode configuration for Linear burst write operations */

    sCommand.OperationType = HAL_OSPI_OPTYPE_WRITE_CFG;

    sCommand.Instruction = LINEAR_BURST_WRITE;

    sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_SRAM_WRITE;

     

    if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {

    Error_Handler();

    }

    /* Memory-mapped mode configuration for Linear burst read operations */

    sCommand.OperationType = HAL_OSPI_OPTYPE_READ_CFG;

    sCommand.Instruction = LINEAR_BURST_READ;

    sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_SRAM_READ;

     

    if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) {

    Error_Handler();

    }

    /*Disable timeout counter for memory mapped mode*/

    sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;

    /*Enable memory mapped mode*/

    if (HAL_OSPI_MemoryMapped(&hospi1, &sMemMappedCfg) != HAL_OK) {

    Error_Handler();

    }

    }

     

     

    KvangAuthor
    Graduate II
    August 23, 2024

    Hi Kaouthar
    Thanks for the help

    I got it working ok now.

    I do have a igniring problem, when I debug the debugger read and potentially write to the memory(fpga)

    but when doing so it's not setting  a cmd, just leave it 0x00

    I use the CMD from the exsample:

    #define LINEAR_BURST_READ 0x20

    #define LINEAR_BURST_WRITE 0xA0

    at first that would lock up my system all the time if the mouse hit a variable from the octospi1 memmory area.

    i then added 0x00 as read in the fpga.

    but if I change a variable i can see that it tries to write, but cmd is also 0x00 there.

    1. what method is the debugger using? it seems like can I chose the CMD for that?
    2. are there a different way to differ is it's a read or a write? e.g. adding pull-up or down to RWDS

    thanks in advance

    Kristian Vang

    Technical Moderator
    August 27, 2024

    Hi @Kvang ,

     

    Thank you for coming back to the community and glad to know that the initial issue is solved.

    I didn't quite understand your new questions and your requests. 

    For HyperBus protocol, during this initial phase, the OCTOSPI sends 48 bits over IO[7:0] to specify the operations
    to be performed with the external device. And Bit 47 identifies the transaction as a read or a write.

    KDJEM1_0-1724769081034.png

    Please take a look at RM0468 section 25 Octo-SPI interface (OCTOSPI) may help you.

    Thank you.

    Kaouthar

     

    KvangAuthorAnswer
    Graduate II
    September 10, 2024

    Hi 
    I found that the reason for the last problem was that I was holding the bus from the FPGA, so problem solved, I now have a Stable OctoSpi <> FPGA interface.

    Explorer II
    November 1, 2024
     
    Glad to see you successfully implemented OSPI communication between STM32 and FPGA. 
    I also want to implement such a communication protocol on the PCB I made myself,
    but I am stuck at the output stage of the microcontroller.
    I think it is incorrect configuration that causes my microcontroller to not be able to send data.
    Could you please let me refer to your configuration or project?
    I really need your help. Thanks in advance.