0
\$\begingroup\$

I'm trying to implement a synchronous up-down counter in verilog with the following rules:

  • Counter only changes on rising edge of clock
  • When reset=1, count goes to 00, normal operation when reset=0
  • counting is enabled when en=1 and disabled when en=0 counts up when dir=0 counts down when dir=1
  • counting does NOT wrap. When counting down, does NOT go from 00 to 11 (i.e. stays at 00). When counting up, does NOT go from 11 to 00 (i.e. stays at 11)

So far my code is:

module counter
(
    input               clock,
    input               reset,
    input               en,
    input               dir,
    output reg [1:0]    count
);

  always @ (posedge clock) begin
    if (reset==1'b1 || en==1'b0) begin 
      count=2'b0; 
    end

    else if(en==1 && dir==0) begin //up count when dir=0
      count <= count+1;
    end

    else begin //down count when dir=1
      count <=count-1; 
    end

  end

endmodule

This does everything except for the counting wraps. How would I make it not wrap?

Testbench below(comments say what the code should be doing)

`timescale 1 ns / 1 ns    // Time for each step, time precision
`define CLOCK_PERIOD        10

module main;          
    reg         clock;      // Free running clock
    reg         reset;      // Input - reset
    reg         en;         // Input - enable counting
    reg         dir;        // Input - direction (0=up)
    wire [1:0]  count;      // Output - Counter output

    // Instantiate module
    counter    u1( clock, reset, en, dir, count );

    initial                 // Done once at start up 
    begin
        $display( "Up/Down Counter (no wrap)" );
        $dumpfile( "dump.vcd" ); 
        $dumpvars;

        // Set initial values for inputs, asset reset
        clock = 0;          
        reset = 1;              
        en = 0;
        dir = 0;

        // Turn off reset after one clock
        #(`CLOCK_PERIOD * 1)
        reset = 0;

        // Wait for a while to make sure it does not count
        #(`CLOCK_PERIOD * 2)

        // Assert enable, should count up to 3 and stop
        en = 1;
        #(`CLOCK_PERIOD * 5)

        // Make sure does not count if we now deassert enable
        en = 0;
        #(`CLOCK_PERIOD * 2)

        // Set dir=1 but en=0, so should not change
        dir = 1;
        #(`CLOCK_PERIOD * 2)

        // Now enable, should count down and stop at 0
        en = 1;

        // Count back down, check if reset is ignored if not asserted 
        // at a clock edge
        dir=1;
        #7
        reset = 1;
        #6
        reset = 0;

        #(`CLOCK_PERIOD * 5)
        reset = 0;

        // Leave time to stabilize
        #(`CLOCK_PERIOD * 4)
        $finish;
   end
\$\endgroup\$
  • \$\begingroup\$ Why do you have an asynchronous reset when the requirements says only on the rising edge of a clock? What is the point of the final else count = 2'b0;? You want it to do a reset if en is low? Where is your logic to compare count to the min/max? \$\endgroup\$ – Greg Apr 1 '16 at 17:03
  • 1
    \$\begingroup\$ Hint: many counters offer a "terminal count" (TC) output, which is active when the counter reaches its maximum value, and the next step would cause wrapping. \$\endgroup\$ – The Photon Apr 1 '16 at 17:06
  • \$\begingroup\$ @Greg I've edited the code it so only is triggered on the rising edge of the clock. The last else count = 2'b0 was a silly mistake (I was trying to have that be the en condition). Yes, I want to reset when en is low (edited code should now reflect that). I think the logic to compare count is one of my issues. I'm not sure even where to start to have my counter stop after one iteration... \$\endgroup\$ – tlb Apr 1 '16 at 17:21
  • \$\begingroup\$ Why do you expect it to stop counting? Your code says "when enabled and direction is 0, count up. Otherwise count down." Where in that do you tell it to not increment before it overflows. You are also setting count to zero when not enabled - surely an "enable" shouldn't reset the value otherwise it would be a reset signal. \$\endgroup\$ – Tom Carpenter Apr 1 '16 at 17:29
  • \$\begingroup\$ @Tom Carpenter I've posted the testbench that the code needs to pass, perhaps that will answer the first question. My problem lies in the fact that I don't know where to start in order to tell it to not increment before it overflows.... Also, I suppose you're right. I just assumed en would go to zero, since that's the easiest. \$\endgroup\$ – tlb Apr 1 '16 at 17:43
3
\$\begingroup\$

Not wrapping is simply a matter of adding checks to see if it would wrap, and inhibiting the counting in that case:

module counter
(
    input               clock,
    input               reset,
    input               en,
    input               dir,
    output reg [1:0]    count
);

  always @ (posedge clock) begin
    if (reset == 1'b1) begin 
      count <= 2'b0; 
    end else if (en == 1'b1) begin
      if (dir == 0) begin
        // up count when dir=0 AND count < max value (all ones)
        if (count != 2'b11) count <= count + 1;
      end else begin
        // down count when dir=1 AND count > min value (all zeros)
        if (count != 2'b00) count <= count - 1;
      end
    end
  end

endmodule

P.S. I'm not sure why you were resetting count when en == 0, so I've eliminated that here.

\$\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.