cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H735G-DK: MDMA Linked List Transfer Causing Choppy ADC Output to HyperRAM

theMcnuggy
Visitor

I'm building a high-speed two-stage data pipeline on the STM32H735G-DK and encountering issues with corrupted signal output in external memory.

Setup:

  • ADC1 + ADC2 in dual interleaved mode sampling a single direct channel at 12-bit resolution

  • Triggered via TIM2 TRGO at 5.5 MHz (effective 11 MSPS)

  • DMA1_Stream0 in double buffer circular mode, writing to RAM_EXEC (bufferA and bufferB)

  • MDMA in circular linked list mode, triggered by DMA TC flag, alternately transfers bufferA/B to external HyperRAM (mapped at 0x70000000 via OCTOSPI2)

Problem:

ADC data is correctly written to internal RAM, but the signal in HyperRAM is choppy, discontinuous, and out of phase when reconstructing a clean 10 kHz sine wave during post-processing.

Suspicions:

  • MDMA may be pulling stale buffer data due to desync with DMA toggle

  • Linked list nodes may be misaligned

  • Memory attribute or caching problems (all regions currently non-cacheable)

What I’ve Tried:

  • Slowing down sampling and verifying CT bit toggling confirms ping-pong behavior

  • DCache cleaning on MDMA nodes and buffers

  • Not enabling I/D cache at all

  • Using HAL_MDMA_Start() for initial transfer, relying on linked list mode after

  • Verified MDMA populates HyperRAM—but signal remains malformed

  • Replaced MDMA with memcpy() inside the DMA IRQ: slightly better, still drops frames

  • Replaced MDMA with HAL_DMAEx_ChangeMemory() to write directly to HyperRAM: also choppy

Any insight on whether this is an MDMA sync issue, a cache config issue, or something else would be appreciated.

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define DMA_BUF_SIZE 8192
#define MDMA_TOTAL_BLOCKS    ((16 * 1024 * 1024) / (DMA_BUF_SIZE * 4))
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
DMA_HandleTypeDef hdma_adc1;

OSPI_HandleTypeDef hospi2;

SPI_HandleTypeDef hspi5;

TIM_HandleTypeDef htim2;

/* USER CODE BEGIN PV */
MDMA_HandleTypeDef hmdma;
MDMA_LinkNodeConfTypeDef nodeConfig;
ALIGN_32BYTES(MDMA_LinkNodeTypeDef mdmaNodes[MDMA_TOTAL_BLOCKS]);

__IO uint32_t *mem_addr = (uint32_t *)OCTOSPI2_BASE;
__IO uint32_t *pRam = (uint32_t *)OCTOSPI2_BASE;
ALIGN_32BYTES(uint32_t __attribute__((section (".RAM_EXEC"))) bufferA[DMA_BUF_SIZE]);
ALIGN_32BYTES(uint32_t __attribute__((section (".RAM_EXEC"))) bufferB[DMA_BUF_SIZE]);
uint32_t idx = 0;
volatile uint32_t counter = 0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_ADC2_Init(void);
static void MX_OCTOSPI2_Init(void);
static void MX_SPI5_Init(void);
static void MX_TIM2_Init(void);
/* USER CODE BEGIN PFP */
static void MPU_Config_Test(void);
static void CPU_CACHE_Enable(void);
void writeToSPI5(uint16_t a, uint16_t b);
void MDMA_Config_Test(void);
HAL_StatusTypeDef ADC_MultiModeMultiBufferStart_DMA(ADC_HandleTypeDef *hadc, const uint32_t *pData, const uint32_t *pDataSecond, uint32_t Length);

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
	MPU_Config_Test();
	CPU_CACHE_Enable();

	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;  // Enable DWT
	DWT->CYCCNT = 0;                                 // Reset counter
	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;             // Start counting
  /* USER CODE END 1 */

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_OCTOSPI2_Init();
  MX_SPI5_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  MDMA_Config_Test();

  for (idx = 0; idx < 0x400000; idx++)
  {
	   *mem_addr = 0;
	   mem_addr++;
  }
  for (idx = 0; idx < DMA_BUF_SIZE; idx++)
  {
	  bufferA[idx] = 0;
	  bufferB[idx] = 0;
  }

  mem_addr = (__IO uint32_t *)(OCTOSPI2_BASE);
  pRam = (__IO uint32_t *)(OCTOSPI2_BASE);

  if (HAL_ADC_Start(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }
  if (ADC_MultiModeMultiBufferStart_DMA(&hadc1,
                                   (uint32_t *)bufferA,(uint32_t *)bufferB,
								   DMA_BUF_SIZE
                                  ) != HAL_OK)
  {
    Error_Handler();
  }

  DWT->CYCCNT = 0;                                 // Reset counter

  if (HAL_TIM_Base_Start(&htim2) != HAL_OK)
  {
    Error_Handler();
  }

  HAL_Delay(5000);

  __HAL_MDMA_DISABLE(&hmdma);

  if (HAL_TIM_Base_Stop(&htim2) != HAL_OK)
  {
	  Error_Handler();
  }
  counter = 0;
//  HAL_Delay(5000);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if (counter >= 0xFFFF)
		{
			counter = 0;
		}
		if (pRam >= 0x71000000)
		{
			pRam = (__IO uint32_t *)(OCTOSPI2_BASE);
		}
		writeToSPI5(0xFE6B, 0x2840);
		writeToSPI5(counter++, 0xB00B);
		writeToSPI5(0xB00B, 0xB00B);
     	        writeToSPI5(0xB00B, 0xB00B);
		writeToSPI5(*pRam >> 16, *pRam);
    	        pRam++;
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_DIRECT_SMPS_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 2;
  RCC_OscInitStruct.PLL.PLLN = 44;
  RCC_OscInitStruct.PLL.PLLP = 1;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief Peripherals Common Clock Configuration
  * @retval None
  */
void PeriphCommonClock_Config(void)
{
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Initializes the peripherals clock
  */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_SPI5;
  PeriphClkInitStruct.PLL3.PLL3M = 5;
  PeriphClkInitStruct.PLL3.PLL3N = 64;
  PeriphClkInitStruct.PLL3.PLL3P = 2;
  PeriphClkInitStruct.PLL3.PLL3Q = 32;
  PeriphClkInitStruct.PLL3.PLL3R = 2;
  PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_2;
  PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE;
  PeriphClkInitStruct.PLL3.PLL3FRACN = 0;
  PeriphClkInitStruct.Spi45ClockSelection = RCC_SPI45CLKSOURCE_PLL3;
  PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL3;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}
/**
  * @brief OCTOSPI2 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_OCTOSPI2_Init(void)
{

  /* USER CODE BEGIN OCTOSPI2_Init 0 */
  OSPI_HyperbusCmdTypeDef sCommand;
  OSPI_MemoryMappedTypeDef sMemMappedCfg;
  /* USER CODE END OCTOSPI2_Init 0 */

  OSPIM_CfgTypeDef sOspiManagerCfg = {0};
  OSPI_HyperbusCfgTypeDef sHyperBusCfg = {0};

  /* USER CODE BEGIN OCTOSPI2_Init 1 */

  /* USER CODE END OCTOSPI2_Init 1 */
  /* OCTOSPI2 parameter configuration*/
  hospi2.Instance = OCTOSPI2;
  hospi2.Init.FifoThreshold = 4;
  hospi2.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
  hospi2.Init.MemoryType = HAL_OSPI_MEMTYPE_HYPERBUS;
  hospi2.Init.DeviceSize = 24;
  hospi2.Init.ChipSelectHighTime = 8;
  hospi2.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
  hospi2.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
  hospi2.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
  hospi2.Init.ClockPrescaler = 1;
  hospi2.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
  hospi2.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE;
  hospi2.Init.ChipSelectBoundary = 23;
  hospi2.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED;
  hospi2.Init.MaxTran = 0;
  hospi2.Init.Refresh = 250;
  if (HAL_OSPI_Init(&hospi2) != HAL_OK)
  {
    Error_Handler();
  }
  sOspiManagerCfg.ClkPort = 2;
  sOspiManagerCfg.DQSPort = 2;
  sOspiManagerCfg.NCSPort = 2;
  sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_2_LOW;
  sOspiManagerCfg.IOHighPort = HAL_OSPIM_IOPORT_2_HIGH;
  if (HAL_OSPIM_Config(&hospi2, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }
  sHyperBusCfg.RWRecoveryTime = 3;
  sHyperBusCfg.AccessTime = 6;
  sHyperBusCfg.WriteZeroLatency = HAL_OSPI_LATENCY_ON_WRITE;
  sHyperBusCfg.LatencyMode = HAL_OSPI_FIXED_LATENCY;
  if (HAL_OSPI_HyperbusCfg(&hospi2, &sHyperBusCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN OCTOSPI2_Init 2 */
  /* Memory-mapped mode configuration --------------------------------------- */
  sCommand.AddressSpace = HAL_OSPI_MEMORY_ADDRESS_SPACE; // indicates the address space accessed by the command
  sCommand.AddressSize  = HAL_OSPI_ADDRESS_32_BITS; // indicates the size of the address.
  sCommand.DQSMode      = HAL_OSPI_DQS_ENABLE; // enables or not the data strobe management.
  sCommand.Address      = 0; // contains the address to be sent to the device.
  sCommand.NbData       = 1; // indicates the number of data transferred with this command. This field is only used for indirect mode. This parameter can be a value between 1 and 0xFFFFFFFF

  if (HAL_OSPI_HyperbusCmd(&hospi2, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    Error_Handler();
  }

  sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE; // Specifies if the timeout counter is enabled to release the chip select

  if (HAL_OSPI_MemoryMapped(&hospi2, &sMemMappedCfg) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE END OCTOSPI2_Init 2 */

}


/**
  * @brief TIM2 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 49;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);

  DMA1_Stream0->CR |= DMA_SxCR_TCIE;

}
/**
  * @brief GPIO Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOG_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */
HAL_StatusTypeDef ADC_MultiModeMultiBufferStart_DMA(ADC_HandleTypeDef *hadc, const uint32_t *pData, const uint32_t *pDataSecond, uint32_t Length)
{
  HAL_StatusTypeDef tmp_hal_status;
  ADC_HandleTypeDef tmphadcSlave;
  ADC_Common_TypeDef *tmpADC_Common;

  /* Check the parameters */
  assert_param(IS_ADC_MULTIMODE_MASTER_INSTANCE(hadc->Instance));
  assert_param(IS_FUNCTIONAL_STATE(hadc->Init.ContinuousConvMode));
  assert_param(IS_ADC_EXTTRIG_EDGE(hadc->Init.ExternalTrigConvEdge));

  if (LL_ADC_REG_IsConversionOngoing(hadc->Instance) != 0UL)
  {
    return HAL_BUSY;
  }
  else
  {
    /* Process locked */
    __HAL_LOCK(hadc);

    /* Case of ADC slave using its own DMA channel: check whether handle selected
       corresponds to ADC master or slave instance */
    if (__LL_ADC_MULTI_INSTANCE_MASTER(hadc->Instance) != hadc->Instance)
    {
      /* Case of ADC slave selected: enable ADC instance */
      tmp_hal_status = ADC_Enable(hadc);
    }
    else
    {
      tmphadcSlave.State = HAL_ADC_STATE_RESET;
      tmphadcSlave.ErrorCode = HAL_ADC_ERROR_NONE;
      /* Set a temporary handle of the ADC slave associated to the ADC master   */
      ADC_MULTI_SLAVE(hadc, &tmphadcSlave);

      if (tmphadcSlave.Instance == NULL)
      {
        /* Set ADC state */
        SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);

        /* Process unlocked */
        __HAL_UNLOCK(hadc);

        return HAL_ERROR;
      }

      /* Enable the ADC peripherals: master and slave (in case if not already   */
      /* enabled previously)                                                    */
      tmp_hal_status = ADC_Enable(hadc);
      if (tmp_hal_status == HAL_OK)
      {
        tmp_hal_status = ADC_Enable(&tmphadcSlave);
      }
    }

    /* Start multimode conversion of ADCs pair */
    if (tmp_hal_status == HAL_OK)
    {
      /* Set ADC state */
      ADC_STATE_CLR_SET(hadc->State,
                        (HAL_ADC_STATE_READY | HAL_ADC_STATE_REG_EOC | HAL_ADC_STATE_REG_OVR | HAL_ADC_STATE_REG_EOSMP),
                        HAL_ADC_STATE_REG_BUSY);

      /* Set ADC error code to none */
      ADC_CLEAR_ERRORCODE(hadc);

      /* Set the DMA transfer complete callback */
      hadc->DMA_Handle->XferCpltCallback = ADC_DMAConvCplt;

      /* Set the DMA half transfer complete callback */
      hadc->DMA_Handle->XferHalfCpltCallback = ADC_DMAHalfConvCplt;

      /* Set the DMA error callback */
      hadc->DMA_Handle->XferErrorCallback = ADC_DMAError ;

      /* Manage ADC and DMA start: ADC overrun interruption, DMA start, ADC     */
      /* start (in case of SW start):                                           */

      /* Clear regular group conversion flag and overrun flag */
      /* (To ensure of no unknown state from potential previous ADC operations) */
      __HAL_ADC_CLEAR_FLAG(hadc, (ADC_FLAG_EOC | ADC_FLAG_EOS | ADC_FLAG_OVR));

      /* Process unlocked */
      /* Unlock before starting ADC conversions: in case of potential         */
      /* interruption, to let the process to ADC IRQ Handler.                 */
      __HAL_UNLOCK(hadc);

      /* Enable ADC overrun interrupt */
      __HAL_ADC_ENABLE_IT(hadc, ADC_IT_OVR);

      /* Case of ADC slave using its own DMA channel: check whether handle selected
         corresponds to ADC master or slave instance */
      if (__LL_ADC_MULTI_INSTANCE_MASTER(hadc->Instance) != hadc->Instance)
      {
        /* Case of ADC slave selected: Start the DMA channel. */
        /* Note: Data transfer will start upon next call of this function using handle of ADC master */
        tmp_hal_status = HAL_DMA_Start_IT(hadc->DMA_Handle, (uint32_t)&hadc->Instance->DR, (uint32_t)pData, Length);
      }
      else
      {
        /* Pointer to the common control register  */
        tmpADC_Common = __LL_ADC_COMMON_INSTANCE(hadc->Instance);

        /* Start the DMA channel */
        tmp_hal_status = HAL_DMAEx_MultiBufferStart(hadc->DMA_Handle, (uint32_t)&tmpADC_Common->CDR, (uint32_t)pData, (uint32_t)pDataSecond, Length);

        /* Enable conversion of regular group.                                    */
        /* If software start has been selected, conversion starts immediately.    */
        /* If external trigger has been selected, conversion will start at next   */
        /* trigger event.                                                         */
        /* Start ADC group regular conversion */
        LL_ADC_REG_StartConversion(hadc->Instance);
      }
    }
    else
    {
      /* Process unlocked */
      __HAL_UNLOCK(hadc);
    }

    /* Return function status */
    return tmp_hal_status;
  }
}

void MDMA_Config_Test(void)
{

    __HAL_RCC_MDMA_CLK_ENABLE();

    hmdma.Instance = MDMA_Channel0;
    hmdma.Init.Request = MDMA_REQUEST_SW;
    hmdma.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER;
    hmdma.Init.Priority = MDMA_PRIORITY_VERY_HIGH;
    hmdma.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
    hmdma.Init.SourceInc = MDMA_SRC_INC_WORD;
    hmdma.Init.DestinationInc = MDMA_DEST_INC_WORD;
    hmdma.Init.SourceDataSize = MDMA_SRC_DATASIZE_WORD;
    hmdma.Init.DestDataSize = MDMA_DEST_DATASIZE_WORD;
    hmdma.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;
    hmdma.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE;
    hmdma.Init.DestBurst = MDMA_DEST_BURST_SINGLE;
    hmdma.Init.BufferTransferLength = 4;
    hmdma.Init.SourceBlockAddressOffset = 0;
    hmdma.Init.DestBlockAddressOffset = 0;


    if (HAL_MDMA_Init(&hmdma) != HAL_OK)
    {
        Error_Handler();
    }

    // Link list node config
	nodeConfig.Init.Request = MDMA_REQUEST_SW;
	nodeConfig.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER;
	nodeConfig.Init.Priority = MDMA_PRIORITY_VERY_HIGH;
	nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
	nodeConfig.Init.SourceInc = MDMA_SRC_INC_WORD;
	nodeConfig.Init.DestinationInc = MDMA_DEST_INC_WORD;
	nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_WORD;
	nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_WORD;
	nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;
	nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE;
	nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE;
	nodeConfig.Init.BufferTransferLength = 4;
	nodeConfig.Init.SourceBlockAddressOffset = 0;
	nodeConfig.Init.DestBlockAddressOffset = 0;
	nodeConfig.PostRequestMaskAddress = (uint32_t)(&(DMA1->LIFCR));
	nodeConfig.PostRequestMaskData = DMA_LIFCR_CTCIF0;

	nodeConfig.BlockDataLength = DMA_BUF_SIZE * 4;  // 256KB per node
	nodeConfig.BlockCount = 1;
	for (int i = 0; i < MDMA_TOTAL_BLOCKS; i++)
    {
        // Alternate between bufferA and bufferB
        if (i % 2 == 0)
        {
        	nodeConfig.SrcAddress = (uint32_t)bufferA;
        }
        else
        {
        	nodeConfig.SrcAddress = (uint32_t)bufferB;
        }
        nodeConfig.DstAddress = (uint32_t)(OCTOSPI2_BASE + (i * DMA_BUF_SIZE * 4));

        // Link to node
        if(HAL_MDMA_LinkedList_CreateNode(&mdmaNodes[i], &nodeConfig) != HAL_OK)
        {
        	Error_Handler();
        }
        if(HAL_MDMA_LinkedList_AddNode(&hmdma, &mdmaNodes[i],0) != HAL_OK)
        {
        	Error_Handler();
        }
    }
	if (HAL_MDMA_LinkedList_EnableCircularMode(&hmdma) != HAL_OK)
	{
	  Error_Handler();
	}
	for(int i = 0; i < MDMA_TOTAL_BLOCKS; i++)
	{
		SCB_CleanDCache_by_Addr((uint32_t*)&mdmaNodes[i], sizeof(MDMA_LinkNodeTypeDef));
	}
	SCB_CleanDCache_by_Addr((uint32_t*)bufferA, DMA_BUF_SIZE * sizeof(uint32_t));
	SCB_CleanDCache_by_Addr((uint32_t*)bufferB, DMA_BUF_SIZE * sizeof(uint32_t));

    // Start MDMA in circular linked-list mode
	if (HAL_MDMA_Start(&hmdma,
	                   (uint32_t)&bufferA,
	                   (uint32_t)OCTOSPI2_BASE,
	                   (DMA_BUF_SIZE * sizeof(uint32_t)),
	                   1) != HAL_OK)
	{
	    Error_Handler();
	}

}

void writeToSPI5(uint16_t a, uint16_t b)
{
	// --> put data to TXDR --> TXFIFO Buffer
	while(((SPI5->SR) & SPI_SR_TXP) != SPI_SR_TXP){}
	*((__IO uint16_t*)&SPI5->TXDR) = a;
	*((__IO uint16_t*)&SPI5->TXDR) = b;
}

static void CPU_CACHE_Enable(void)
{
  /* Enable I-Cache */
  SCB_EnableICache();

  /* Enable D-Cache */
  SCB_EnableDCache();
}

static void MPU_Config_Test(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct;

  /* Disable the MPU */
  HAL_MPU_Disable();

  /* Configure the MPU as Strongly ordered for not defined regions */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = 0x00;
  MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
  MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.SubRegionDisable = 0x87;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);

  /* Configure the MPU attributes as WT for OCTOSPI2 */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = OCTOSPI2_BASE;
  MPU_InitStruct.Size = MPU_REGION_SIZE_64MB;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; // make sure to perform cache maintenance if we stop sampling then resume sampling
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER1;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);

  // Configure RAM_D1 (RAM_EXEC) as full access, non-cacheable (or cacheable if you're managing coherency)
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = D1_AXISRAM_BASE;
  MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;  // round up to cover 320KB
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // make sure to perform cache maintenance if we stop sampling then resume sampling
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER2;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);

  /* Enable the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/* USER CODE END 4 */

 /* MPU Configuration */

void MPU_Config(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct = {0};

  /* Disables the MPU */
  HAL_MPU_Disable();

  /** Initializes and configures the Region and the memory to be protected
  */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x0;
  MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
  MPU_InitStruct.SubRegionDisable = 0x87;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);
  /* Enables the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @PAram  file: pointer to the source file name
  * @PAram  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

Has anyone implemented a similar architecture on the H7 successfully and seen this behavior? Could this be an issue with buffer synchronization, MDMA trigger timing, or maybe how the HAL MDMA linked list is set up?

Any help or suggestions would be appreciated.

0 REPLIES 0