How to modify the timers to buffer the compare value?


Emperor Napoleon

Recommended Posts

Hi, the emperor would like to ask for your thoughts on how best to buffer the ZPUino's compare values (prescaler too but less critical). Yes, I am a VHDL n00b.

 

The idea is that any compare value settings would not take effect until the next overflow event. This is helpful when changing the frequency of the timer to avoid unnatural states.

 

Below is a diff on how to turn a "timer" into a "timer2", the difference being, a "timer2" should buffer its prescaler and compare registers (just like the PWM channels).

 

 

I am not sure if anything is wrong with the below code, although i have run into an apparent issue where I hear audio pops when i listen to PWM output and I make little changes to the timer frequency

 

* Not actually an audio project, this is just how I debug.

* It doesn't happen when buffering is disabled, or when I set the update mode to "NOW", or when I just swap in the "timer" for the "timer2"

* I still think buffering is important for smooth frequency adjustments without occasional glitches ..... is this true?

 

 

That is unfortunately all I know... I don't know how to simulate anything and I don't know whats going on inside the fpga's head. Nothing helpful to be seen on the scope because the pops are too brief. 

 

 

The emperor would like to thank you for any insights, advice, or harsh criticism. :D

 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.numeric_std.all;  library work; use work.zpu_config.all; use work.zpupkg.all; use work.zpuinopkg.all; -entity timer is+entity timer2 is   generic (     TSCENABLED: boolean := false;     PWMCOUNT: integer range 1 to 8 := 2;     WIDTH: integer range 1 to 32 := 16;     PRESCALER_ENABLED: boolean := true;     BUFFERS: boolean := true   );   port (     wb_clk_i:   in std_logic;        wb_rst_i:   in std_logic;@@ -59,23 +59,23 @@     wb_adr_i:   in std_logic_vector(5 downto 0);     wb_we_i:    in std_logic;     wb_cyc_i:   in std_logic;     wb_stb_i:   in std_logic;     wb_ack_o:   out std_logic;     wb_inta_o:  out std_logic;      pwm_out:    out std_logic_vector(PWMCOUNT-1 downto 0)    );-end entity timer;+end entity timer2; -architecture behave of timer is+architecture behave of timer2 is    component prescaler is   port (     clk:    in std_logic;     rst:    in std_logic;     prescale:   in std_logic_vector(2 downto 0);     event:  out std_logic                          );   end component prescaler; @@ -93,20 +93,22 @@   ccm:  std_logic; -- clear on compare match   en:   std_logic; -- enable   dir:  std_logic; -- direction   ien:  std_logic; -- interrupt enable   intr: std_logic; -- interrupt   pres: std_logic_vector(2 downto 0); -- Prescaler   updp: std_logic_vector(1 downto 0);   presrst: std_logic;   pwmr: pwmregs;   pwmrb:pwmregs;+  cmpb:  unsigned(WIDTH-1 downto 0);+  presb: std_logic_vector(2 downto 0); end record;  constant UPDATE_NOW: std_logic_vector(1 downto 0) := "00"; constant UPDATE_ZERO_SYNC: std_logic_vector(1 downto 0) := "01"; constant UPDATE_LATER: std_logic_vector(1 downto 0) := "10";   signal tmr0_prescale_rst: std_logic; --signal tmr0_prescale: std_logic_vector(2 downto 0); signal tmr0_prescale_event: std_logic;@@ -214,50 +216,58 @@      do_interrupt <= '0';      if wb_rst_i='1' then         w.en := '0';         w.ccm := '0';         w.dir := '0';         w.ien := '0';         w.pres := (others => '0');         w.presrst := '1';-        w.updp := UPDATE_ZERO_SYNC;+        w.updp := UPDATE_NOW;         for i in 0 to PWMCOUNT-1 loop           w.pwmrb(i).en :='0';           w.pwmr(i).en :='0';         end loop;      else       if do_interrupt='1' then         w.intr := '1';       end if;        w.presrst := '0';        -- Wishbone access       if write_ctrl='1' then         w.en  := wb_dat_i(0);         w.ccm := wb_dat_i(1);         w.dir := wb_dat_i(2);         w.ien := wb_dat_i(3);+        if BUFFERS then+          w.presb:= wb_dat_i(6 downto 4);+        else         w.pres:= wb_dat_i(6 downto 4);+        end if;         w.updp := wb_dat_i(10 downto 9);          if wb_dat_i(7)='0' then           w.intr:='0';         end if;       end if;        if write_cmp='1' then+        if BUFFERS then+          w.cmpb := unsigned(wb_dat_i(WIDTH-1 downto 0));+        else         w.cmp := unsigned(wb_dat_i(WIDTH-1 downto 0));       end if;+      end if;        if write_cnt='1' then         w.cnt := unsigned(wb_dat_i(WIDTH-1 downto 0));       else         if tmrr.en='1' and tmr0_prescale_event='1' then           -- If output matches, set interrupt           if ovf='1' then             if tmrr.ien='1' then               do_interrupt<='1';             end if;@@ -313,23 +323,27 @@          end if;         end if;       end loop;     end if;      if BUFFERS then     for i in 0 to PWMCOUNT-1 loop       case tmrr.updp is         when UPDATE_NOW =>           w.pwmr(i) := tmrr.pwmrb(i);+          w.cmp := w.cmpb;+          w.pres := w.presb;         when UPDATE_ZERO_SYNC =>           if ovf='1' then             w.pwmr(i) := tmrr.pwmrb(i);+            w.cmp := w.cmpb;+            w.pres := w.presb;           end if;         when UPDATE_LATER =>           --if wb_adr_i(3 downto 2) = std_logic_vector(to_unsigned(i,2)) then           --  if wb_adr_i(1 downto 0)="11" then           --    w.pwmr(i) := tmrr.pwmrb(i);           --  end if;          -- end if;          when others =>           --w.pwmr(i) := tmrr.pwmrb(i);
Link to comment
Share on other sites

Is the emperor sure that he really wants that behavior?

 

If the timer is initially set to a really long period (or maybe worse, not running at all) and then a short period is requested then you might be waiting a long time for the interrupts to start occurring at the new frequency

Link to comment
Share on other sites

Is the emperor sure that he really wants that behavior?

 

If the timer is initially set to a really long period (or maybe worse, not running at all) and then a short period is requested then you might be waiting a long time for the interrupts to start occurring at the new frequency

 

Right, I have some logic in C that enables the buffering only if the last period set is below some threshold.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.