Monostable multivibrator simulation

Viewed 69

The monostable module implements a monostable multivibrator. It takes in three inputs (clk, reset, trigger) and outputs a single (pulse).

The trigger input triggers the module. When triggered, the output signal (pulse) will switch to a high position for a period of clock ticks, and then return to a low position. The pulse width of the output signal can be set via the PULSE_WIDTH parameter, which represents the period in clock ticks that the signal should stay high.

Now, what is happening is when it gets triggered, immediately output signal (pulse) is getting high.

When triggered, the output signal (pulse) should be high on the next active edge of clk. What changes can be done ?

module monostable(
        input clk,
        input reset,
        input trigger,
        output reg pulse = 0
);
        parameter PULSE_WIDTH = 20;

        reg [4:0] count = 0;

        wire countReset = reset | (count == PULSE_WIDTH);

        always @(posedge trigger, posedge countReset) begin
                if (countReset) begin
                        pulse <= 1'b0;
                end else begin
                        pulse <= 1'b1;
                end
        end

        always @(posedge clk, posedge countReset) begin
                if(countReset) begin
                        count <= 0;
                end else begin
                        if(pulse) begin
                        count <= count + 1'b1;
                        end
                end
        end
endmodule



module monostable_tb();

reg clk;
reg reset;
reg trigger;
wire pulse;

parameter PULSE_WIDTH = 20;

monostable imonostable(.*);

initial begin
    clk=0;
    forever #50 clk=~clk;
end

initial begin
    $monitor("trigger=%b  pulse=%b, count = %0d",trigger,pulse,imonostable.count);
    $dumpfile("monostable_tb.vcd");
    $dumpvars(0,monostable_tb);

    trigger=1'b0;
    reset = 1'b0;    

    @(posedge clk) reset = 1'b1;
    @(posedge clk) reset = 1'b0;  
    @(posedge clk) trigger=1'b1;
    @(posedge clk) trigger=1'b0;

    repeat(25) @(posedge clk);

    $finish;
end  
endmodule

enter image description here

2 Answers

If a 1-clock synchronous delay is needed, then run the output thru a flip flop.

    module monostable(
        input clk,
        input reset,
        input trigger,
        output reg pulse = 0
);
        parameter PULSE_WIDTH = 20;

        reg [4:0] count = 0;
        reg reg_pulse_temp;

        wire countReset = reset | (count == PULSE_WIDTH);
      
        always @(posedge trigger, posedge countReset) begin
                if (countReset) begin
                        reg_pulse_temp <= 1'b0;
                end else begin
                        reg_pulse_temp <= 1'b1;
                end
        end

        always @(posedge clk, posedge countReset) begin
                if(countReset) begin
                        count <= 0;
                        // reset the flop
                        pulse <= 0;
                end else begin
                        if(pulse) begin
                        count <= count + 1'b1;
                        end
                end
          
          // flip flop
          pulse  <= reg_pulse_temp;
          
        end
endmodule

Here is a simplified design which sets the output one cycle after the input trigger pulse, and keeps the output high for 20 cycles:

   module monostable(
            input clk,
            input reset,
            input trigger,
            output reg pulse = 0
    );
            parameter PULSE_WIDTH = 20;
    
            reg [4:0] count;
    
            always @(posedge clk, posedge reset) begin
                    if (reset) begin
                            pulse <= 1'b0;
                    end else if (trigger) begin
                            pulse <= 1'b1;
                    end else if (count == PULSE_WIDTH-1) begin
                            pulse <= 1'b0;
                    end
            end
    
            always @(posedge clk, posedge reset) begin
                    if(reset) begin
                            count <= 0;
                    end else if (pulse) begin
                            count <= count + 1'b1;
                    end
            end
    endmodule

It now uses a single clock signal, instead of 2. It also uses a simple reset signal, which can avoid potential glitches causing unwanted asynchronous resets.

You should also use nonblocking assignments in the testbench:

@(posedge clk) reset  <= 1'b1;
@(posedge clk) reset  <= 1'b0;  
@(posedge clk) trigger<= 1'b1;
@(posedge clk) trigger<= 1'b0;
Related