dif_pwm.h

To use this DIF, include the following C header:

#include "/workspace/sw/device/lib/dif/dif_pwm.h"

This header provides the following device interface functions:

Generated from dif_pwm.h
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_PWM_H_
#define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_PWM_H_

/**
 * @file
 * @brief <a href="/hw/ip/pwm/doc/">PWM</a> Device Interface Functions
 *
 * The PWM block contains a 16-bit "phase counter" that controls each channel's
 * output signal. The "phase counter" acts as a point of reference for a single
 * PWM "pulse cycle", that is further broken into "beats", depending on the
 * `beats_per_cycle` parameter. Specifically, a "pulse cycle" may contain [2,
 * 2^16] "beats". Within a "pulse cycle", users can configure the duty cycle in
 * number of "beats". Additionally, the duration of a single "beat", is computed
 * by dividing the core clock frequency by `clock_divisor + 1`.
 *
 *       PWM "pulse cycle" defined by 16-bit phase counter
 *  ___________________________________________________________
 *  |                                                         |
 *  v                                                         v
 * |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-| ... |-|-|-|-|
 *
 * |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-| ... |-|-|-|-|
 * min beat size = 1 phase counter tick --> 2^16 "beats" / "pulse cycle"
 *
 * |---------------------------------------------- ... | ... --|
 *    max beat size = 2^15 phase counter ticks --> 2 "beats" / "pulse cycle"
 */

#include <stdint.h>

#include "sw/device/lib/dif/autogen/dif_pwm_autogen.h"

#ifdef __cplusplus
extern "C" {
#endif  // __cplusplus

/**
 * Helper X macro for defining enums and case statements related to PWM
 * channels. If an additional channel is ever added to the hardware, this list
 * can be updated.
 */
#define DIF_PWM_CHANNEL_LIST(X) \
  X(0)                          \
  X(1)                          \
  X(2)                          \
  X(3)                          \
  X(4)                          \
  X(5)

/**
 * Helper macro for defining a `dif_pwm_channel_t` enumeration constant.
 * @channel_ PWM channel of the enumeration constant.
 */
#define PWM_CHANNEL_ENUM_INIT_(channel_) \
  kDifPwmChannel##channel_ = 1U << channel_,

/**
 * A PWM channel.
 */
typedef enum dif_pwm_channel {
  DIF_PWM_CHANNEL_LIST(PWM_CHANNEL_ENUM_INIT_)
} dif_pwm_channel_t;

#undef PWM_CHANNEL_ENUM_INIT_

/**
 * Runtime configuration for PWM.
 *
 * This struct describes runtime configuration for one-time configuration of the
 * PWM "pulse cycle" and "beat" durations that impact all PWM channels.
 */
typedef struct dif_pwm_config {
  /**
   * The core clock frequency divisor that determines the period of a single
   * "beat" within a PWM "pulse cycle".
   *
   * Valid range: [0, 2^26)
   *
   * A value of zero, configures the "beat" period to the core clock period.
   */
  uint32_t clock_divisor;
  /**
   * The total number of "beats" in a "pulse cycle", including both "on" and
   * "off" beats in a "pulse cycle".
   *
   * Valid range: [2, 2^16]
   *
   * Note: while any value in the range is acceptable, values will be rounded
   * down to closest power-of-two.
   *
   * A "beat" represents a unit of time of the PWM output signal. Higher values
   * provide higher duty cycle resolutions, at the expense of longer "pulse
   * cycles", while lower values provide shorter "pulse cycles", at the expense
   * of lower duty cycle resolutions (since duty cycles are configured in
   * "beats" / "pulse cycle").
   */
  uint32_t beats_per_pulse_cycle;
} dif_pwm_config_t;

/**
 * A PWM channel mode.
 */
typedef enum dif_pwm_mode {
  /**
   * The PWM duty cycle is set by the firmware and remains constant.
   */
  kDifPwmModeFirmware = 0,
  /**
   * The PWM duty cycle linearly sweeps between both primary and secondary
   * firmware-configured values, based on a firmware-configured step size.
   */
  kDifPwmModeHeartbeat = 1,
  /**
   * The PWM duty cycle alternates between both primary and secondary
   * firmware-configured values, based on two firmware-configured durations.
   */
  kDifPwmModeBlink = 2,
} dif_pwm_mode_t;

/**
 * A PWM channel polarity.
 */
typedef enum dif_pwm_polarity {
  /**
   * A PWM signal is active-high.
   */
  kDifPwmPolarityActiveHigh = 0,
  /**
   * A PWM signal is active-low.
   */
  kDifPwmPolarityActiveLow = 1,
} dif_pwm_polarity_t;

/**
 * Runtime configuration for a specific PWM channel.
 *
 * This struct describes runtime configuration for one-time configuration of a
 * specific PWM channel.
 */
typedef struct dif_pwm_channel_config {
  /**
   * Primary duty cycle, in number of "beats" / "pulse cycle".
   *
   * Valid range: [0, beats_per_pulse_cycle)
   *
   * Note: the raw value written to the `A_*` bitfield in each PWM channel's
   *       `DUTY_CYCLE_*` CSR is in units of "phase counter ticks", not "beats".
   *       However, the hardware only takes into account the first `DC_RESN` + 1
   *       MSBs of the raw duty cycle value to determine the number of "beats"
   *       for a given duty cycle. To make this configuration easier, the
   *       software manages the conversion from "beats_per_cycle" to
   *       "phase_counter_ticks" under the hood.
   */
  uint16_t duty_cycle_a;
  /**
   * Secondary duty cycle, in number of "beats" / "pulse cycle", that is only
   * relevant in heartbeat and blink modes.
   *
   * Valid range: [0, beats_per_pulse_cycle)
   *
   * Note: above notes for `duty_cycle_a` apply here too.
   */
  uint16_t duty_cycle_b;
  /**
   * Phase delay at the beginning of a "pulse cycle" to delay the active
   * duty cycle "beats" for, in number of "beats".
   *
   * Valid range: [0, beats_per_pulse_cycle)
   */
  uint16_t phase_delay;
  /**
   * The operation mode to configure the channel in, see `dif_pwm_mode_t`.
   */
  dif_pwm_mode_t mode;
  /**
   * The polarity to configure the channel in, see `dif_pwm_polarity_t`.
   */
  dif_pwm_polarity_t polarity;
  /**
   * One of two blink parameters that only impact the "Heartbeat" and "Blink"
   * operation modes.
   *
   * The meaning of this parameter is different based on the operation mode:
   *     - Heartbeat mode: determines the number of "pulse cycles" between
   *                       increments/decrements to the duty cycle.
   *
   *     - Blink mode: determines the number of "pulse cycles" to pulse at duty
   *                   cycle A, before switching to duty cycle B.
   */
  uint16_t blink_parameter_x;
  /**
   * One of two blink parameters that only impact the "Heartbeat" and "Blink"
   * operation modes.
   *
   * The meaning of this parameter is different based on the operation mode:
   *     - Heartbeat mode: determines the increment/decrement amount in number
   *                       of duty cycle "beats".
   *
   *     - Blink mode: determines the number of "pulse cycles" to pulse at duty
   *                   cycle B, before switching to duty cycle A.
   *
   * Note: the raw value written to the `blink_parameter_x` bitfield in each PWM
   * channel's `BLINK_PARAM_*` CSR is in units of "phase counter ticks", not
   * "beats". However, for ease of configuration, the software manages this
   * conversion under the hood.
   */
  uint16_t blink_parameter_y;
} dif_pwm_channel_config_t;

/**
 * Configures "phase cycle" and "beat" durations of all PWM channels.
 *
 * Since changes to `CLK_DIV` and `DC_RESN` are only allowed when the PWM is
 * disabled, this function has the side effect of temporarily disabling all PWM
 * channels while configurations are updated, before restoring the original
 * enablement state.
 *
 * This function should only need to be called once for the lifetime of
 * `handle`.
 *
 * @param pwm A PWM handle.
 * @param config Runtime configuration parameters.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_configure(const dif_pwm_t *pwm, dif_pwm_config_t config);

/**
 * Configures a single PWM channel.
 *
 * Since changes to `CLK_DIV` and `DC_RESN` are only allowed when the PWM is
 * disabled, this function has the side effect of temporarily disabling the
 * PWM while configurations are updated, before returning the block to its
 * original enablement state.
 *
 * This function should only need to be called once for each PWM channel that
 * will be used.
 *
 * @param pwm A PWM handle.
 * @param channel A PWM channel to configure.
 * @param config Runtime configuration parameters for the channel.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_configure_channel(const dif_pwm_t *pwm,
                                       dif_pwm_channel_t channel,
                                       dif_pwm_channel_config_t config);

/**
 * Sets the enablement state of the PWM phase counter, which controls the
 * enablement of all PWM channels.
 *
 * @param pwm A PWM handle.
 * @param enabled The enablement state to configure the PWM phase counter in.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_phase_cntr_set_enabled(const dif_pwm_t *pwm,
                                            dif_toggle_t enabled);

/**
 * Gets the enablement state of the PWM phase counter, which controls the
 * enablement of all PWM channels.
 *
 * @param pwm A PWM handle.
 * @param[out] is_enabled The enablement state of the PWM phase counter.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_phase_cntr_get_enabled(const dif_pwm_t *pwm,
                                            dif_toggle_t *is_enabled);

/**
 * Sets the enablement states of one or more PWM channels.
 *
 * @param pwm A PWM handle.
 * @param channels The channels to enable (one or more `dif_pmw_channel_t`s
 *                 ORed together.)
 * @param enabled The enablement state to set.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_channel_set_enabled(const dif_pwm_t *pwm,
                                         uint32_t channels,
                                         dif_toggle_t enabled);

/**
 * Gets the enablement state of one PWM channel.
 *
 * @param pwm A PWM handle.
 * @param channel The PWM channel to get the enablement state of.
 * @param[out] is_enabled The enablement state of the PWM channel.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_channel_get_enabled(const dif_pwm_t *pwm,
                                         dif_pwm_channel_t channel,
                                         dif_toggle_t *is_enabled);

/**
 * Locks PWM configurations.
 *
 * This function is reentrant: calling it while locked will have no effect and
 * return `kDifOk`.
 *
 * @param pwm A PWM handle.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_lock(const dif_pwm_t *pwm);

/**
 * Checks whether PWM configurations are locked.
 *
 * @param pwm A PWM handle.
 * @param[out] is_locked Out-param for the locked state.
 * @return The result of the operation.
 */
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_is_locked(const dif_pwm_t *pwm, bool *is_locked);

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus

#endif  // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_PWM_H_