From 5e5b929ee6329984f2b7acea8d55489f8ca2e6d9 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 22 Mar 2023 14:52:38 +0100 Subject: [PATCH] soc/cores/pwm: Add MultiChannelPWM core reusing PWM module. Also do minor changes to PWM module to allow reuse. --- litex/soc/cores/pwm.py | 80 +++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/litex/soc/cores/pwm.py b/litex/soc/cores/pwm.py index 1cea99109..20818ecfe 100644 --- a/litex/soc/cores/pwm.py +++ b/litex/soc/cores/pwm.py @@ -19,7 +19,7 @@ class PWM(Module, AutoCSR): Pulse Width Modulation can be useful for various purposes: dim leds, regulate a fan, control an oscillator. Software can configure the PWM width and period and enable/disable it. """ - def __init__(self, pwm=None, clock_domain="sys", with_csr=True, + def __init__(self, pwm=None, clock_domain="sys", counter=None, with_csr=True, default_enable = 0, default_width = 0, default_period = 0): @@ -32,30 +32,34 @@ class PWM(Module, AutoCSR): # # # - counter = Signal(32, reset_less=True) - sync = getattr(self.sync, clock_domain) + + # PWM Counter/Period logic. + if counter is None: + self.counter = counter = Signal(32, reset_less=True) + sync += [ + counter.eq(0), + If(self.enable & ~self.reset, + If(counter < (self.period - 1), + counter.eq(counter + 1) + ) + ) + ] + + # PWM Width logic. sync += [ + pwm.eq(0), If(self.enable & ~self.reset, - counter.eq(counter + 1), If(counter < self.width, pwm.eq(1) - ).Else( - pwm.eq(0) - ), - If(counter >= (self.period - 1), - counter.eq(0) ) - ).Else( - counter.eq(0), - pwm.eq(0) ) ] if with_csr: self.add_csr(clock_domain) - def add_csr(self, clock_domain): + def add_enable_width_csr(self, clock_domain): self._enable = CSRStorage(description="""PWM Enable.\n Write ``1`` to enable PWM.""", reset = self.enable.reset) @@ -63,13 +67,55 @@ class PWM(Module, AutoCSR): Defines the *Duty cycle* of the PWM. PWM is active high for *Width* ``{cd}_clk`` cycles and active low for *Period - Width* ``{cd}_clk`` cycles.""".format(cd=clock_domain), reset = self.width.reset) - self._period = CSRStorage(32, reset_less=True, description="""PWM Period.\n - Defines the *Period* of the PWM in ``{cd}_clk`` cycles.""".format(cd=clock_domain), - reset = self.period.reset) n = 0 if clock_domain == "sys" else 2 self.specials += [ MultiReg(self._enable.storage, self.enable, n=n), MultiReg(self._width.storage, self.width, n=n), - MultiReg(self._period.storage, self.period, n=n), ] + + def add_period_csr(self, clock_domain): + self._period = CSRStorage(32, reset_less=True, description="""PWM Period.\n + Defines the *Period* of the PWM in ``{cd}_clk`` cycles.""".format(cd=clock_domain), + reset = self.period.reset) + + n = 0 if clock_domain == "sys" else 2 + self.specials += MultiReg(self._period.storage, self.period, n=n) + + def add_csr(self, clock_domain): + self.add_enable_width_csr(clock_domain) + self.add_period_csr(clock_domain) + +# Multi Channel Pulse Width Modulation ------------------------------------------------------------- + +class MultiChannelPWM(Module, AutoCSR): + """Multi-Channel Pulse Width Modulation + + PWM module with Multi-Channel support. + """ + def __init__(self, pads, clock_domain="sys", + default_enable = 0, + default_width = 0, + default_period = 0): + + # # # + + nchannels = len(pads) + + counter = Signal(32, reset_less=True) + for n in range(nchannels): + pwm = PWM( + pwm = pads[n], + clock_domain = clock_domain, + with_csr = False, + counter = None if n == 0 else counter, + default_enable = default_enable, + default_width = default_width, + default_period = default_period, + ) + + if n == 0: + self.comb += counter.eq(pwm.counter) + pwm.add_period_csr(clock_domain) + pwm.add_enable_width_csr(clock_domain) + self.add_module(name=f"channel{n}", module=pwm)