Merge branch 'main' into sw_cleanup

This commit is contained in:
stnolting 2025-04-12 23:03:12 +02:00
commit ab6bca3bb4
7 changed files with 133 additions and 30 deletions

View file

@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 12.04.2025 | 1.11.2.7 | :sparkles: add PWM polarity configuration | [#1230](https://github.com/stnolting/neorv32/pull/1230) |
| 07.04.2025 | 1.11.2.6 | :bug: fix SDI input synchronization | [#1227](https://github.com/stnolting/neorv32/pull/1227) |
| 05.04.2025 | 1.11.2.5 | minor rtl edits and optimizations | [#1225](https://github.com/stnolting/neorv32/pull/1225) |
| 01.04.2025 | 1.11.2.4 | :bug: fix bug in PWM clock prescaler | [#1222](https://github.com/stnolting/neorv32/pull/1222) |

View file

@ -30,13 +30,14 @@ Depending on the configured number channels, the PWM module provides 16 configur
be accessed without raising an exception. However, registers above `IO_PWM_NUM_CH-1` are read-only and hardwired to
all-zero.
Each configuration provides a 1-bit enable flag to enable/disable the according channel, an 8-bit register for setting
the duty cycle and a 3-bit clock prescaler select as well as a 10-bit clock diver for _coarse_ and _fine_ tuning of the
carrier frequency, respectively.
Each configuration provides a 1-bit enable flag to enable/disable the according channel, a 1-bit flag for setting the
channel polarity, an 8-bit register for setting the duty cycle and a 3-bit clock prescaler select as well as a 10-bit clock
diver for _coarse_ and _fine_ tuning of the carrier frequency, respectively.
A channel is enabled by setting the `PWM_CFG_EN` bit. If this bit is cleared the according PWM output is set to zero.
The duty cycle is programmed via the 8 `PWM_CFG_DUTY` bits. Based on the value programmed to this bits the duty cycle
the resulting duty cycle of the according channel can be computed by the following formula:
A channel is enabled by setting the `PWM_CFG_EN` bit. If this bit is cleared the according PWM output is deasserted
(zero if channel polarity is not inverted, one if inverted). The duty cycle is programmed via the 8 `PWM_CFG_DUTY` bits.
Based on the value programmed to these bits the resulting duty cycle of the according channel can be computed by the
following formula:
_Duty Cycle_[%] = `PWM_CFG_DUTY` / 2^8^
@ -66,13 +67,15 @@ _f~PWM~_[Hz] = _f~main~_[Hz] / (2^8^ * `clock_prescaler` * (1 + `PWM_CFG_CDIV`))
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.5+<| `0xfff00000` .5+<| `CHANNEL_CFG[0]` <|`31` - `PWM_CFG_EN` ^| r/w <| Channel 0: channel enabled when set
<|`30:28` - `PWM_CFG_PRSC_MSB:PWM_CFG_PRSC_LSB` ^| r/w <| Channel 0: 3-bit clock prescaler select
<|`27:18` ^| r/- <| Channel 0: _reserved_, hardwired to zero
<|`27` - `PWM_CFG_POL` ^| r/w <| Channel 0: channel polarity, inverted when set
<|`26:18` ^| r/- <| Channel 0: _reserved_, hardwired to zero
<|`17:8` - `PWM_CFG_CDIV_MSB:PWM_CFG_CDIV_LSB` ^| r/w <| Channel 0: 10-bit clock divider
<|`7:0` - `PWM_CFG_DUTY_MSB:PWM_CFG_DUTY_LSB` ^| r/w <| Channel 0: 8-bit duty cycle
| `0xfff00004` ... `0xfff00038` | `CHANNEL_CFG[1]` ... `CHANNEL_CFG[14]` | ... | r/w <| Channels 1 to 14
.5+<| `0xfff0003C` .5+<| `CHANNEL_CFG[15]` <|`31` - `PWM_CFG_EN` ^| r/w <| Channel 15: channel enabled when set
<|`30:28` - `PWM_CFG_PRSC_MSB:PWM_CFG_PRSC_LSB` ^| r/w <| Channel 15: 3-bit clock prescaler select
<|`27:18` ^| r/- <| Channel 15: _reserved_, hardwired to zero
<|`27` - `PWM_CFG_POL` ^| r/w <| Channel 15: channel polarity, inverted when set
<|`26:18` ^| r/- <| Channel 15: _reserved_, hardwired to zero
<|`17:8` - `PWM_CFG_CDIV_MSB:PWM_CFG_CDIV_LSB` ^| r/w <| Channel 15: 10-bit clock divider
<|`7:0` - `PWM_CFG_DUTY_MSB:PWM_CFG_DUTY_LSB` ^| r/w <| Channel 15: 8-bit duty cycle
|=======================

View file

@ -29,7 +29,7 @@ package neorv32_package is
-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01110206"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01110207"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

View file

@ -158,6 +158,7 @@ architecture neorv32_pwm_channel_rtl of neorv32_pwm_channel is
-- configuration register --
signal cfg_en : std_ulogic; -- channel enable
signal cfg_prsc : std_ulogic_vector(2 downto 0); -- (course) clock prescaler select
signal cfg_pol : std_ulogic; -- channel polarity
signal cfg_cdiv : std_ulogic_vector(9 downto 0); -- (fine) clock divider
signal cfg_duty : std_ulogic_vector(7 downto 0); -- duty cycle
@ -175,12 +176,14 @@ begin
if (rstn_i = '0') then
cfg_en <= '0';
cfg_prsc <= (others => '0');
cfg_pol <= '0';
cfg_cdiv <= (others => '0');
cfg_duty <= (others => '0');
elsif rising_edge(clk_i) then
if (we_i = '1') then
cfg_en <= wdata_i(31);
cfg_prsc <= wdata_i(30 downto 28);
cfg_pol <= wdata_i(27);
cfg_cdiv <= wdata_i(17 downto 8);
cfg_duty <= wdata_i(7 downto 0);
end if;
@ -188,7 +191,7 @@ begin
end process config_write;
-- read access --
rdata_o <= cfg_en & cfg_prsc & "0000000000" & cfg_cdiv & cfg_duty when (re_i = '1') else (others => '0');
rdata_o <= cfg_en & cfg_prsc & cfg_pol & "000000000" & cfg_cdiv & cfg_duty when (re_i = '1') else (others => '0');
-- enable global clock generator --
clkgen_en_o <= cfg_en;
@ -226,9 +229,9 @@ begin
-- pwm output --
if (cfg_en = '0') or (unsigned(cnt_duty) >= unsigned(cfg_duty)) then
pwm_o <= '0';
pwm_o <= cfg_pol; -- deasserted
else
pwm_o <= '1';
pwm_o <= not cfg_pol; -- asserted
end if;
end if;

View file

@ -14,6 +14,7 @@
#ifndef NEORV32_PWM_H
#define NEORV32_PWM_H
#include <stdbool.h>
#include <stdint.h>
@ -36,6 +37,7 @@ enum CHANNEL_CFG_enum {
PWM_CFG_CDIV_LSB = 8, /**< PWM configuration register(8) (r/w): Clock divider (10-bit), LSB */
PWM_CFG_CDIV_MSB = 17, /**< PWM configuration register(17) (r/w): Clock divider (10-bit), MSB */
PWM_CFG_POL = 27, /**< PWM configuration register(27) (r/w): Channel polarity, inverted when set */
PWM_CFG_PRSC_LSB = 28, /**< PWM configuration register(28) (r/w): Clock prescaler select (3-bit), LSB */
PWM_CFG_PRSC_MSB = 30, /**< PWM configuration register(30) (r/w): Clock prescaler select (3-bit), MSB */
PWM_CFG_EN = 31 /**< PWM configuration register(31) (r/w): channel enable */
@ -51,6 +53,7 @@ int neorv32_pwm_available(void);
int neorv32_pmw_get_num_channels(void);
void neorv32_pwm_ch_enable(int channel);
void neorv32_pwm_ch_disable(int channel);
void neorv32_pwm_ch_set_polarity(int channel, bool inverted);
void neorv32_pwm_ch_set_clock(int channel, int prsc, int cdiv);
void neorv32_pwm_ch_set_duty(int channel, int duty);
/**@}*/

View file

@ -80,6 +80,24 @@ void neorv32_pwm_ch_disable(int channel) {
}
/**********************************************************************//**
* Set PWM channel's polarity configuration.
*
* @param[in] channel Channel select (0..15).
* @param[in] normal polarity if false (default), inverted polarity if true
**************************************************************************/
void neorv32_pwm_ch_set_polarity(int channel, bool inverted) {
channel &= 0xf; // constrain range
if (inverted) {
NEORV32_PWM->CHANNEL_CFG[channel] |= ((uint32_t)(1 << PWM_CFG_POL));
} else {
NEORV32_PWM->CHANNEL_CFG[channel] &= ~((uint32_t)(1 << PWM_CFG_POL));
}
}
/**********************************************************************//**
* Set PWM channel's clock configuration.
*
@ -92,7 +110,7 @@ void neorv32_pwm_ch_set_clock(int channel, int prsc, int cdiv) {
channel &= 0xf; // constrain range
uint32_t tmp = NEORV32_PWM->CHANNEL_CFG[channel];
tmp &= 0x800000ffU; // clear current prsc and cdiv, keep enable and duty
tmp &= 0x880000ffU; // clear current prsc and cdiv, keep enable, polarity, and duty
tmp |= ((uint32_t)(prsc & 0x7U)) << PWM_CFG_PRSC_LSB;
tmp |= ((uint32_t)(cdiv & 0x3ffU)) << PWM_CFG_CDIV_LSB;
NEORV32_PWM->CHANNEL_CFG[channel] = tmp;

View file

@ -528,43 +528,118 @@
<addressBlock>
<offset>0</offset>
<size>0x10</size>
<size>0x40</size>
<usage>registers</usage>
</addressBlock>
<registers>
<register>
<name>CTRL</name>
<description>Control register</description>
<name>CHANNEL_CFG[0]</name>
<description>Channel 0 configuration register</description>
<addressOffset>0x00</addressOffset>
<fields>
<field>
<name>PWM_CTRL_EN</name>
<bitRange>[0:0]</bitRange>
<description>PWM controller enable flag</description>
<name>PWM_CFG_DUTY</name>
<bitRange>[7:0]</bitRange>
<description>Duty cycle</description>
</field>
<field>
<name>PWM_CTRL_PRSCx</name>
<bitRange>[3:1]</bitRange>
<name>PWM_CFG_CDIV</name>
<bitRange>[17:8]</bitRange>
<description>Clock divider</description>
</field>
<field>
<name>PWM_CFG_POL</name>
<bitRange>[27:27]</bitRange>
<description>Channel polarity, inverted when set</description>
</field>
<field>
<name>PWM_CFG_PRSC</name>
<bitRange>[30:28]</bitRange>
<description>Clock prescaler select</description>
</field>
<field>
<name>PWM_CFG_EN</name>
<bitRange>[31:31]</bitRange>
<description>Channel enable</description>
</field>
</fields>
</register>
<register>
<name>DC[0]</name>
<description>Duty cycle register 0</description>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[1]</name>
<description>Channel 1 configuration register</description>
<addressOffset>0x04</addressOffset>
</register>
<register>
<name>DC[1]</name>
<description>Duty cycle register 1</description>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[2]</name>
<description>Channel 2 configuration register</description>
<addressOffset>0x08</addressOffset>
</register>
<register>
<name>DC[2]</name>
<description>Duty cycle register 2</description>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[3]</name>
<description>Channel 3 configuration register</description>
<addressOffset>0x0C</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[4]</name>
<description>Channel 4 configuration register</description>
<addressOffset>0x10</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[5]</name>
<description>Channel 5 configuration register</description>
<addressOffset>0x14</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[6]</name>
<description>Channel 6 configuration register</description>
<addressOffset>0x18</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[7]</name>
<description>Channel 7 configuration register</description>
<addressOffset>0x1C</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[8]</name>
<description>Channel 8 configuration register</description>
<addressOffset>0x20</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[9]</name>
<description>Channel 9 configuration register</description>
<addressOffset>0x24</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[10]</name>
<description>Channel 10 configuration register</description>
<addressOffset>0x28</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[11]</name>
<description>Channel 11 configuration register</description>
<addressOffset>0x2C</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[12]</name>
<description>Channel 12 configuration register</description>
<addressOffset>0x30</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[13]</name>
<description>Channel 13 configuration register</description>
<addressOffset>0x34</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[14]</name>
<description>Channel 14 configuration register</description>
<addressOffset>0x38</addressOffset>
</register>
<register derivedFrom="CHANNEL_CFG[0]">
<name>CHANNEL_CFG[15]</name>
<description>Channel 15 configuration register</description>
<addressOffset>0x3C</addressOffset>
</register>
</registers>
</peripheral>