You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2021/12/25 09:04:24 UTC

[GitHub] [incubator-nuttx] jlaitine commented on a change in pull request #4384: Mpfs core pwm

jlaitine commented on a change in pull request #4384:
URL: https://github.com/apache/incubator-nuttx/pull/4384#discussion_r775122905



##########
File path: arch/risc-v/src/mpfs/mpfs_corepwm.c
##########
@@ -0,0 +1,787 @@
+/****************************************************************************
+ * arch/risc-v/src/mpfs/mpfs_corepwm.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+#include <assert.h>
+#include <time.h>
+#include <inttypes.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/timers/pwm.h>
+
+#include <arch/board/board.h>
+
+#include "hardware/mpfs_corepwm.h"
+
+#include "riscv_arch.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef OK
+#  define OK 0
+#endif
+
+/* This module only compiles if at least one CorePWM instance
+ * is configured to the FPGA
+ */
+
+#ifndef CONFIG_MPFS_HAVE_COREPWM
+#  error This should not be compiled as CorePWM block is not defined/configured
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct mpfs_pwmchan_s
+{
+  uint8_t channel;                     /* Timer output channel: {1,..16} */
+};
+
+/* This structure represents the state of one PWM timer */
+
+struct mpfs_pwmtimer_s
+{
+  FAR const struct pwm_ops_s *ops;     /* PWM operations */
+  uint8_t nchannels;                   /* Number of channels on this PWM block */
+  uint8_t pwmid;                       /* PWM ID {1,...} */
+  struct mpfs_pwmchan_s channels[MPFS_MAX_PWM_CHANNELS];
+  uint32_t frequency;                  /* Current frequency setting */
+  uintptr_t base;                      /* The base address of the pwm block */
+  uint32_t pwmclk;                     /* The frequency of the pwm clock */
+};
+
+/****************************************************************************
+ * Static Function Prototypes
+ ****************************************************************************/
+
+/* Register access */
+
+static uint32_t pwm_getreg(struct mpfs_pwmtimer_s *priv, int offset);
+static void pwm_putreg(struct mpfs_pwmtimer_s *priv, int offset,
+                       uint32_t value);
+
+#ifdef CONFIG_DEBUG_PWM_INFO
+static void pwm_dumpregs(struct mpfs_pwmtimer_s *priv, FAR const char *msg);
+#else
+#  define pwm_dumpregs(priv,msg)
+#endif
+
+/* Timer management */
+
+static int pwm_timer(FAR struct mpfs_pwmtimer_s *priv,
+                     FAR const struct pwm_info_s *info);
+
+/* PWM driver methods */
+
+static int pwm_setup(FAR struct pwm_lowerhalf_s *dev);
+static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev);
+
+static int pwm_start(FAR struct pwm_lowerhalf_s *dev,
+                     FAR const struct pwm_info_s *info);
+
+static int pwm_stop(FAR struct pwm_lowerhalf_s *dev);
+static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev,
+                     int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is the list of lower half PWM driver methods used by the upper half
+ * driver
+ */
+
+static const struct pwm_ops_s g_pwmops =
+{
+  .setup       = pwm_setup,
+  .shutdown    = pwm_shutdown,
+  .start       = pwm_start,
+  .stop        = pwm_stop,
+  .ioctl       = pwm_ioctl,
+};
+
+#ifdef CONFIG_MPFS_COREPWM0
+static struct mpfs_pwmtimer_s g_pwm0dev =
+{
+  .ops         = &g_pwmops,
+  .nchannels   = CONFIG_MPFS_COREPWM0_NCHANNELS,
+  .pwmid       = 0,
+  .channels    =
+  {
+    {
+    .channel = 1
+    },
+    {
+     .channel = 2
+    },
+    {
+     .channel = 3
+    },
+    {
+     .channel = 4
+    },
+    {
+     .channel = 5
+    },
+    {
+     .channel = 6
+    },
+    {
+     .channel = 7
+    },
+    {
+     .channel = 8
+    },
+    {
+     .channel = 9
+    },
+    {
+     .channel = 10
+    },
+    {
+     .channel = 11
+    },
+    {
+     .channel = 12
+    },
+    {
+     .channel = 13
+    },
+    {
+     .channel = 14
+    },
+    {
+     .channel = 15
+    },
+    {
+     .channel = 16
+    }
+  },
+  .base        = CONFIG_MPFS_COREPWM0_BASE,
+  .pwmclk      = CONFIG_MPFS_COREPWM0_PWMCLK,
+};
+#endif
+
+#ifdef CONFIG_MPFS_COREPWM1
+static struct mpfs_pwmtimer_s g_pwm1dev =
+{
+  .ops         = &g_pwmops,
+  .nchannels   = CONFIG_MPFS_COREPWM1_NCHANNELS,
+  .pwmid       = 1,
+  .channels    =
+  {
+    {
+    .channel = 1
+    },
+    {
+     .channel = 2
+    },
+    {
+     .channel = 3
+    },
+    {
+     .channel = 4
+    },
+    {
+     .channel = 5
+    },
+    {
+     .channel = 6
+    },
+    {
+     .channel = 7
+    },
+    {
+     .channel = 8
+    },
+    {
+     .channel = 9
+    },
+    {
+     .channel = 10
+    },
+    {
+     .channel = 11
+    },
+    {
+     .channel = 12
+    },
+    {
+     .channel = 13
+    },
+    {
+     .channel = 14
+    },
+    {
+     .channel = 15
+    },
+    {
+     .channel = 16
+    }
+  },
+  .base        = CONFIG_MPFS_COREPWM1_BASE,
+  .pwmclk      = CONFIG_MPFS_COREPWM1_PWMCLK,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pwm_getreg
+ *
+ * Description:
+ *   Read the value of an PWM timer register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the PWM block status
+ *   offset - The offset to the register to read
+ *
+ * Returned Value:
+ *   The current contents of the specified register
+ *
+ ****************************************************************************/
+
+static uint32_t pwm_getreg(struct mpfs_pwmtimer_s *priv, int offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: pwm_putreg
+ *
+ * Description:
+ *   Read the value of an PWM timer register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the PWM block status
+ *   offset - The offset to the register to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void pwm_putreg(struct mpfs_pwmtimer_s *priv, int offset,
+                       uint32_t value)
+{
+  /* TODO: 8,16 & 32 bit reg width consideration
+   * 32 bit access is required for a 32 bit register
+   */
+
+  putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: pwm_dumpregs
+ *
+ * Description:
+ *   Dump all timer registers.
+ *
+ * Input Parameters:
+ *   priv - A reference to the PWM block status
+ *
+ * Returned Value:
+ *   None
+ *
+ * TODO: Add TACH* register if tachometer feature is taken in use
+ * TODO: Add DAC* register if DA feature is taken in use
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_PWM_INFO
+#define MPFS_PWMREG_STEP (MPFS_COREPWM_PWM2_POS_EDGE_OFFSET -  MPFS_COREPWM_PWM1_POS_EDGE_OFFSET)
+
+static void pwm_dumpregs(struct mpfs_pwmtimer_s *priv, FAR const char *msg)
+{
+  pwminfo("%s:\n", msg);
+  pwminfo("  PRESCALE: %08x PERIOD: %08x\n",
+          pwm_getreg(priv, MPFS_COREPWM_PRESCALE_OFFSET),
+          pwm_getreg(priv, MPFS_COREPWM_PERIOD_OFFSET));
+  pwminfo("  SYNC_UPDATE: %02x\n",
+          pwm_getreg(priv, MPFS_COREPWM_SYNC_UPDATE_OFFSET));
+  pwminfo("  PWM_ENABLE_0_7: %02x PWM_ENABLE_8_15: %02x\n",
+          pwm_getreg(priv, MPFS_COREPWM_PWM_ENABLE_0_7_OFFSET),
+          pwm_getreg(priv, MPFS_COREPWM_PWM_ENABLE_8_15_OFFSET));
+
+  for (int i = 0; i < priv->nchannels; i++)
+    {
+      pwminfo("  PWM%d_POSEDGE: %s%08x PWM%d_NEGEDGE: %s%08x\n",
+              i + 1, (i < 9) ? " " : "",
+              pwm_getreg(priv, MPFS_COREPWM_PWM1_POS_EDGE_OFFSET +
+                         i * MPFS_PWMREG_STEP),
+              i + 1, (i < 9) ? " " : "",
+              pwm_getreg(priv, MPFS_COREPWM_PWM1_NEG_EDGE_OFFSET +
+                         i * MPFS_PWMREG_STEP));
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: pwm_timer
+ *
+ * Description:
+ *   (Re-)initialize the timer resources and start the pulsed output
+ *
+ * Input Parameters:
+ *   priv - A reference to the lower half PWM driver state structure
+ *   info - A reference to the characteristics of the pulsed output
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+static int pwm_timer(FAR struct mpfs_pwmtimer_s *priv,
+                     FAR const struct pwm_info_s *info)
+{
+  int      i;
+
+  /* Calculated values */
+
+  /* TODO: We might need to calculate prescaler on some rare cases,
+   * for now hardcoded to 0
+   */
+
+  uint32_t prescaler = 0;
+
+  uint32_t period;
+
+  DEBUGASSERT(priv != NULL && info != NULL);
+  DEBUGASSERT(info->frequency > 0);
+
+  /* CorePWM FPGA block can be configured to be with either 8, 16, or 32 bit
+   * registers width. Minimally PWM functionality is set up by two registers:
+   * PRESCALE and PERIOD which are common to all channels. Up to 16 channels
+   * may be configured in use. Clock used by the block may be selected in
+   * design phase an on Icicle Kit reference design version 21.04 has at
+   * least the following clock signals to choose from the Clocks_and_Resets
+   * block: 125MHz, 100MHz, 75MHz, 62.5MHz, 50MHz, and 25MHz.
+   *
+   * For now only 32 the bit configuration is supported.
+   * TODO: Add 8 and 16 bit width support
+   *
+   * There are many combinations of prescaler and period registers, but the
+   * best will be the one that has the smallest prescaler value. That is the
+   * solution that should give us the most accuracy in the pwm control.
+   *
+   * Example for clk = 25MHz, prescale 0 and 32 bit wide registers:
+   *   PWM period granularity PWM_PG = (PRESCALE + 1) / pwmclk =
+   *   40 ns × 1 = 40 ns, so the smallest step is 40ns
+   *   pwmclk = clk / (PRESCALE + 1) = 25,000,000 / (PRESCALE + 1) =
+   *     25,000,000
+   *
+   *    For desired output frequency of 50Hz and using PRESCALE of 0:
+   *    PERIOD = pwmclk / frequency = 25,000,000 / 50 = 500,000
+   */
+
+  pwminfo("PWM%u frequency: %u PWMCLK: %u prescaler: %u\n",
+          priv->pwmid, info->frequency, priv->pwmclk, prescaler);
+
+  /* Set the reload and prescaler values */
+
+  period = priv->pwmclk / info->frequency;
+
+  pwm_putreg(priv, MPFS_COREPWM_PERIOD_OFFSET, period);
+  pwm_putreg(priv, MPFS_COREPWM_PRESCALE_OFFSET, prescaler);
+
+  /* Handle channel specific setup */
+
+  for (i = 0; i < CONFIG_PWM_NCHANNELS; i++)
+    {
+      ub32_t    duty;
+      uint8_t   channel;
+      uint32_t  neg_edge;
+
+      channel   = info->channels[i].channel;
+
+      /* Duty defined as fraction of 65536, i.e. a value of 1 to 65535
+       * corresponding to a duty cycle of 0.000015 - 0.999984
+       */
+
+      duty      = ub16toub32(info->channels[i].duty);
+      neg_edge  = b32toi(duty * period + b32HALF);
+
+      if (channel == 0)   /* A value of zero means to skip this channel */

Review comment:
       Thanks for pointing this out!




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org