0
\$\begingroup\$

I'm self studying with Chu's FPGA prototyping book. Exercise 4.7.1 asks for a programmable square wave generator:

A programmable square-wave generator is a circuit that can generate a square wave with variable on (i.e., logic 1) and off (i.e., logic 0) intervals. The durations of the intervals are specified by two 4-bit control signals, m and n, which are interpreted as unsigned integers. The on and off intervals are m* 100 ns and n* 100 ns, respectively. Design a programmable square-wave generator circuit. The circuit should be completely synchronous.

I have a completely syncrhonous working solution, but I couldn't find a nice way for it to be cycle accurate, in the sense that the code I like the most uses two more clock cycles to change the logic level of the square wave. I'm pasting the whole code at the end of my question, but what I don't like is the part that does if(clk_tick_count == interval_quant_ns - 1), specifically having to substract 1 from the number of intervals I want to count. I came to this solution after verifying that the solution without the -1 was taking two more clock cycles, and fixed it with the -1. I understand this is because since the update is synchronous I need another full clock cycle for the changes to be reflected in my ouput wave.

I just have a feeling that there ir a nicer way to do this but I can't discuss it with anyone nor have I been lucky searching for nicer code online.

Thanks!

This is my current code:

module sqwaveGen
#( 
parameter interval_quant_ns = 5
)
(
input wire clk,
input wire rst,
output wire wave,
input wire [3:0] on_interval, 
input wire [3:0] off_interval 
);

reg wave_next, wave_reg;
reg [3:0] clk_tick_count, clk_tick_count_next;
reg [3:0] count, count_next;

always @(posedge clk, negedge rst) begin
    if(~rst) begin
        wave_reg <= 0;
        clk_tick_count <= 0;
        count <= 0;
    end else begin
        wave_reg <= wave_next;
        clk_tick_count <= clk_tick_count_next;
        count <= count_next; 
    end
end

always @(*) begin
    wave_next = wave_reg;
    count_next = count;
    clk_tick_count_next = clk_tick_count + 1;

    if(wave_reg == 0 && count == off_interval) begin
        wave_next = 1;
        count_next = 0;
    end

    if(wave_reg == 1 && count == on_interval) begin
        wave_next = 0;
        count_next = 0;
    end                      

    if(clk_tick_count == interval_quant_ns - 1) begin
        count_next = count + 1;
        clk_tick_count_next = 0;
    end

end

assign wave = wave_reg;

endmodule

Output wave of a test for the sake of completeness, showing that this works:

waveform

\$\endgroup\$
1
\$\begingroup\$

If you want to implement something that requires a number of count N (4 for example), you can either count 0, 1, ..., N-1 (example, 0, 1, 2, 3, 0, 1...). Or you can count 1, 2, ..., N (example, 1, 2, 3, 4, 1, 2...). So if you want to end the count at the number N, start the count at 1.

Often, coding styles are just personal preferences. The offset by one condition never bothers me. While I almost always use down counting by habit because there are couple of potential benefits -- it may synthesize to be faster, and, it only requires the count parameter input at the beginning clock (usually not an beneficial feature in a continuous loop like here).

\$\endgroup\$
0
\$\begingroup\$

Well for starters you are using two always blocks where one would be fine. Merging everything into one block gets rid of all those ugly "next" signals.

We can also make the output a reg getting rid of the wave_reg signal.

module sqwaveGen
#( 
parameter interval_quant_ns = 5
)
(
input wire clk,
input wire rst,
output reg wave,
input wire [3:0] on_interval, 
input wire [3:0] off_interval 
);

reg [3:0] clk_tick_count, clk_tick_count;
reg [3:0] count;

always @(posedge clk, negedge rst) begin
    if(~rst) begin
        wave_reg <= 0;
        clk_tick_count <= 0;
        count <= 0;
    end else begin       
        clk_tick_count <= clk_tick_count + 1;

        if(wave == 0 && count == off_interval) begin
            wave <= 1;
            count <= 0;
        end

        if(wave == 1 && count == on_interval) begin
            wave <= 0;
            count <= 0;
        end                      

        if(clk_tick_count == interval_quant_ns - 1) begin
            count <= count + 1;
            clk_tick_count <= 0;
        end

    end
end

endmodule
\$\endgroup\$
0
\$\begingroup\$

You could take the "interval_quant_ns - 1" out of your logic and hide it away at the top of the file where you won't often be looking.

Counting your wave counter down (rioraxe's tip) + a modification to Peter Green's code enables you to trim off a few lines too:

module sqwaveGen #(parameter interval_quant_ns = 5)
  (
  input wire clk,
  input wire rst,
  output reg wave,
  input wire [3:0] on_interval, 
  input wire [3:0] off_interval 
  );
  localparam interval_quant_count_to = interval_quant_ns - 1; /* Shh don't tell anyone I'm here */

  reg [3:0] clk_tick_count;
  reg [3:0] count;

  always @(posedge clk, negedge rst) begin
    if(~rst) begin
        wave <= 0;
        clk_tick_count <= 0;
        count <= 0;
    end
    else begin       
        clk_tick_count <= clk_tick_count + 1;

        if(clk_tick_count == interval_quant_count_to) begin
            count <= count - 1;
            clk_tick_count <= 0;
        end

        if(count == 0) begin // now all you need to keep track of is when the counter hits 0; then you toggle the wave and set the counter up for the next interval
            count <= (wave == 1) ? off_interval : on_interval;
            wave <= ~wave;
        end
    end
  end
endmodule
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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