7

I am an experienced C programmer trying to learn enough Verilog to create a brushless motor controller. I am nearly done except for one detail. I want to do something like this:

reg count;

always @(posedge clk, posedge motorruns)begin

  if("i got here because of motorruns edge")   count <= 200;  

  if(i got here because of clk edge") count <= count - 1'b1;

  if(count = 0)  "do motor is not running stuff here"

end

Essentially, I enter the block when either edge occurs, and once inside, I want to determine how I got there. I know I can do: if(motorruns), but that doesn't help me, because I only know the motor is running if I see an edge. I want to do something like: if @(posedge motorruns). I know I could split this into two always blocks, but then I would be changing count from two different places.

Any ideas?

Velvel
  • 3,591
  • 3
  • 12
  • 30
Tinkerer
  • 69
  • 3
  • Is this for synthesis or simulation? – The Photon Feb 09 '23 at 17:40
  • Synthesis. I don't understand why anyone would want do go through all this trouble just to simulate something. – Tinkerer Feb 09 '23 at 20:28
  • 3
    You write two separate blocks. Yes, you would be changing `count` from two different places, but that's what you're *already* asking the synthesizer to do *anyway*. You're already asking it to change the same register on two different clock edges. If that isn't possible, then asking for the same thing with different words isn't going to help you. – user253751 Feb 10 '23 at 13:38
  • What is generating the `motorruns` signal? – sbell Feb 10 '23 at 15:15
  • 1
    @Tinkerer, generally you use simulation-only code to build testbenches for synthesizable designs. – The Photon Feb 10 '23 at 16:29
  • motorruns was a bad name. motorpulse would have been better. It is one of the Hall effect inputs from the motor. My code is meant to flag an absence of hall effect pulses. – Tinkerer Feb 11 '23 at 13:29

2 Answers2

11

Assuming motorruns is intended to be a signal rather than an asynchronous reset, then for synchronous logic you shouldn't be using the signal in the sensitivity list.

Edge-sensitivity should only be used for the clock signal (posedge clock), and optionally an asynchronous reset signal (to clear or set registers immediately regardless of the clock signal).

Instead you would use a synchronous edge-detector circuit to detect the rising edge of your input signal, and check the output of the edge detector in your counter logic.


So how would you code it?

If your input signal (motorruns) is not synchronous to the clock domain (e.g. it comes from the outside world), then you should first add a synchroniser chain to prevent metastability and/or concurrency issues:

// This is a 2-flop synchroniser chain. Two is usually enough, but you could add a third flop if desired.
reg sigInSync;
reg sigIn;
always @ (posedge clk) begin
    sigInSync <= motorruns;
    sigIn <= sigInSync;
end

The first flop in the above circuit samples the input signal on a rising clock edge. In an ideal world this would be enough. However in reality the input signal could change just as the clock edge occurs, causing the output of the flop to go metastable (literally ends up in a state somewhere between high and low, eventually picking a state after a short time period). The second flop prevents this metastable state from continuing on into the rest of the logic, by not sampling again until the next clock edge (giving time for the first to have stabilised).

Now that we have a synchronous signal, we can detect a rising edge by simply comparing the current state of the signal with its state in the previous clock cycle. If it is currently high, but used to be low, then we have a rising edge (for falling edge detection you'd check if it is currently low but used to be high):

wire risingEdge;

reg sigInDly;
always @ (posedge clk) begin
    sigInDly <= sigIn;
end

assign risingEdge = sigIn && !sigInDly;

This will produce a signal synchronous to clock, which will be high for precisely one clock cycle whenever there is a rising edge on the input signal.

Your counter code then no longer needs to do edge detection, it can simply check on each clock cycle to see if the output of the edge detector is high. So your code would become:

reg [<COUNTER_WIDTH>-1:0] count;

always @(posedge clk) begin

  if (risingEdge) begin
      // Motorruns just went high
      count <= 200;  
  end else /*if(count > 0)*/ begin // Uncomment if you want to prevent the counter wrapping around.
      // Otherwise
      count <= count - 1;
  end
  
  // Could be in a different always block as @toolic suggests:
  if(count = 0) begin
       "do motor is not running stuff here"
  end

end

(Note that your counter needs a width setting, otherwise it's only 1-bit wide!)


You would usually also want to add a reset signal to your code. This could either be synchronous, or asynchronous. For async you would add it into the sensitivity list along with the clock.

always @(posedge clk /*or posedge reset*/) begin //Uncomment the posedge reset check if asynchronous.
    if (reset) begin
        ~ do reset here ~
    end else begin
        ~ not in reset here ~
    end
end

When using asynhronous resets, you usually actually want "async assert, sync deassert" to ensure that all blocks which use the same reset signal come out of reset on the same clock edge. Otherwise you risk concurrency and metastability issues occurring. This is acheived using a reset synchroniser chain:

localparam CHAIN_LEN = 3; //Min 2.
reg [CHAIN_LEN-1:0] resetChain;
wire resetOut;
always @ (posedge clk or posedge resetSource) begin
    if (resetSource) begin
        resetChain <= {(CHAIN_LEN){1'b1}};
    end else begin
        resetChain <= {1'b0,resetChain[CHAIN_LEN-1:1]};
    end
end
// This reset signal will go high immediately when resetSource goes high (async assert), but will 
// go low synchronously to the clock signal approximately CHAIN_LEN clock cycles after resetSource goes low.
assign reset = resetChain[0];
Tom Carpenter
  • 63,168
  • 3
  • 139
  • 196
  • I'm trying to avoid a reset signal. – Tinkerer Feb 09 '23 at 20:26
  • 4
    @Tinkerer resets aren't something to try and avoid. Even if just a power on reset to ensure that everything starts at a known state. You're in the hardware world not software, so you ought to be in the mindset of designing a circuit not writing a program. – Tom Carpenter Feb 09 '23 at 21:18
  • 1
    This is a great, detailed and well thought answer. – Vladimir Cravero Feb 10 '23 at 12:16
  • Tom, Your quick and detailed answer was exactly what I needed. Thanks. I copied it and changed a couple names, and it compiled without even a missing semicolon. – Tinkerer Feb 11 '23 at 13:46
  • Regarding reset: The motor control signals are a combinational output based on the hall inputs. The control input is PWM which doesn't need to be initialized. At startup, the motor (driving a a linear actuator), needs to find out where it is. I am hoping to drive it to either end until it stops, then it should know where it is. I am coding this for an ICE40UP5K which, I am told, powers up with all registers set to zeros. Beyond these details, I don't think i need to reset anything. Experience will tell. Doug – Tinkerer Feb 11 '23 at 20:59
2

I recommend using 2 always blocks:

  1. Assign to count
  2. Check count value

In this way, you are not assigning to count from 2 blocks. There is no penalty to having multiple always blocks, and it is often easier to understand the design if you separate features into multiple blocks.

reg [7:0] count;

always @(posedge clk, posedge motorruns) begin
    if (motorruns) begin
        count <= 200;
    end else begin
        count <= count - 1'b1;
    end
end

always @(posedge clk) begin
    if (count == 0) begin
        // do motor is not running stuff here
    end else begin
        // do something else?
    end
end

In Verilog, there is no need to check if clk is high when you are modelling sequential logic (like flip-flops, counters, etc.). There are other Verilog code examples and explanations.

For example, the 1st block asynchonously sets the count to 200 when motorruns is high, then decrements the count otherwise on each posedge of the clock. You likely need to add a condition if you want the count to reload with 200 rather than overflow.

toolic
  • 5,637
  • 5
  • 20
  • 33