2
\$\begingroup\$

The basic gist is I'm trying to write an audio synthesizer. The note sequencer engine is based on a "tick" of 60Hz. Each note duration is a multiple of 5 ticks, let's call this a beat (even though it's really a thirty-second note). As such, I'd like a 1-clock pulse (strobe?) every tick and a 1-clock pulse every beat (5 ticks). Then I can count beat pulses for note duration. For example, a note could play for 8 beats (40 ticks), then another note for 4 beats (20 ticks), etc. However, I'm struggling to figure out how to do this.

The tick pulse is pretty easy:

  localparam CLOCKS_PER_TICK = 415_667; // 25MHz / 60Hz
  localparam TICK_WIDTH = $clog2(CLOCKS_PER_TICK);
  reg   [TICK_WIDTH-1:0]  r_tick_counter = 0;
  reg                     r_tick_pulse = 0;
  always @(posedge i_clk) begin
    if (r_tick_counter == CLOCKS_PER_TICK) begin
      r_tick_counter <= 0;
      r_tick_pulse <= 1;
    end else begin
      r_tick_counter <= r_tick_counter + 1;
      r_tick_pulse <= 0;
    end
  end

The beat pulse seems like it should pretty straightforward: Count 5 tick pulses. However, it ends up being fairly complicated, which means I'm likely doing it wrong:

  localparam  TICKS_PER_BEAT = 5;
  localparam  BEAT_WIDTH = $clog2(TICKS_PER_BEAT);
  reg   [BEAT_WIDTH-1:0]  r_beat_counter = 0;
  reg                     r_beat_clk = 0;
  reg                     r_last_beat_clk = 0;
  always @(posedge i_clk) begin
    if (r_tick_pulse) begin
      if (r_beat_counter == TICKS_PER_BEAT-1) begin
        r_beat_counter <= 0;
        r_beat_clk <= ~r_beat_clk;
      end else begin
        r_beat_counter <= r_beat_counter + 1;
      end
    end
    r_last_beat_clk <= r_beat_clk;
  end
  wire r_beat_pulse = r_beat_clk != r_last_beat_clk;

The idea here is r_beat_counter counts up on every r_tick_pulse from 0 to 4. However, figuring out how to get a 1-clock wide pulse seems to require making a fake "clock" r_beat_clk and then using r_last_beat_clk to effectively detect any edge (both pos and neg) in this "clock". This feels overly complicated, though. Is there some simpler way to accomplish this?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

There's no need to create an edge detector — you already know where the edges are. Try something like this:

  localparam  TICKS_PER_BEAT = 5;
  localparam  BEAT_WIDTH = $clog2(TICKS_PER_BEAT);
  reg   [BEAT_WIDTH-1:0]  r_beat_counter = 0;
  reg                     r_beat_clk = 0;
  always @(posedge i_clk) begin
    if (r_tick_pulse) begin
      if (r_beat_counter == TICKS_PER_BEAT-1) begin
        r_beat_counter <= 0;
        r_beat_clk <= 1;
      end else begin
        r_beat_counter <= r_beat_counter + 1;
        r_beat_clk <= 0;
      end
    end
  end
  wire r_beat_pulse = r_tick_pulse & r_beat_clk;
\$\endgroup\$
3
  • \$\begingroup\$ Thank you! That will totally work. I think I could even get rid of r_beat_clk and do: wire r_beat_pulse = r_tick_pulse & (r_beat_counter == TICKS_PER_BEAT-1); \$\endgroup\$ Commented Sep 10, 2021 at 2:58
  • \$\begingroup\$ BTW, do I have my terminology right here? Are these called pulses, strobes, or something else? \$\endgroup\$ Commented Sep 10, 2021 at 2:59
  • 1
    \$\begingroup\$ "Pulse" is a little too generic for me. "Strobe" or "enable" is fine -- tick_en, beat_en, etc. \$\endgroup\$
    – Dave Tweed
    Commented Sep 10, 2021 at 3:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.