1
\$\begingroup\$

I want to do synthesizable always block, that would execute code by d1 signal changing (posedge and negedge both). I did attempt to create such block:

`timescale 1ns / 1ps
module device(d1, reset, clk, out);
input d1, clk, reset;
output reg out;
reg tmp_d1;
always @ (posedge clk)
begin
 if(!reset) begin
 if(tmp_d1 != d1) begin
 out = ~out;
 end
 
 tmp_d1 = d1;
 end
 else begin
 out <= 0;
 tmp_d1 <= 0;
 end 
end
endmodule

It works while behavioral simulation in Vivado:

Behavioral simulation in Vivado

The testbench:

`timescale 1ns/1ps
module tb_device; 
reg d1, rst, clk;
wire out;
device device1(clk, rst, d1, out);
initial begin
rst = 1;
clk = 0;
d1 = 0;
#50 rst = 0;
#50 d1 = 1;
#70 d1 = 0;
#50 d1 = 1;
#70 d1 = 0;
end
always #10 clk = ~clk;
endmodule

But behavioral simulation result don't match to the tests on programmed FPGA. See the video of programmed FPGA tests on Google drive: https://drive.google.com/file/d/1IemI0XfxICa0hTU312HD8HoI65PG-0Zi/view?usp=sharing

"Not matching" in missing reaction on switching the switcher in some times. In one time led inverts by switching the DE0 board switch (reacts on d1 switching as in simulation) and another time any reaction have occured. Also, by one switching, led can be starting to light but immediately fades away (I'm seeing only blink and nothing after).

Was used Terasic FPGA board DE0 is equipped by the Intel/Altera FPGA Cyclone III (EP3C16F484C6) and Quartus II 9.0 (Build 132 02/25/2009 SJ Web Edition).

Contraints (qsf file) on Google drive: https://drive.google.com/file/d/111kRlpXRi5njfn2kFEx5hefyj3QEykL_/view?usp=sharing

Problem isn't related to the switch debouncing becouse I already tested this switch after progmming more simple Verilog project and all was match to corresponded Vivado simulation.

The SDC file:

create_clock -name CLK27 -period "27MHz" -waveform {0 18.519} [get_ports clk]
derive_pll_clocks
derive_clock_uncertainty

Wasn't occured any critial warning while the Quartus compilation.

But was occured the ordinary warnings:

  1. Feature LogicLock is not available with your current license;
  2. Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings report for details.

Referring to 2), incomplete set of assignments reason is reporting:

incomplete set of assignments

asked Mar 3 at 9:47
\$\endgroup\$
1
  • 3
    \$\begingroup\$ Please post summaries of the videos and other and relevant information about what you were doing, the expected behaviour and the actual behaviour in your question. People here will be reluctant to download videos and other files from Google drive and other file sharing sites. Furthermore links die over time so having the information in one place allows the question to remain useful in time. \$\endgroup\$ Commented Mar 3 at 10:34

2 Answers 2

1
\$\begingroup\$

Your problem is most likely down to the fact that your button input is an asynchronous signal and you have got no logic to handle that cleanly. You are likely to encounter concurrency/meta-stability issues from your button being asynchronous. There is also the potential for weird behaviour if there is insufficient analogue de-bouncing for the switch.

  • If the button edge occurs at just the right moment, it may make your edge detector logic go metastable from violating setup/hold times because the button input is asynchronous.

  • Again because it is an asynchronous button input and you are feeding it into two different registers, it is possible to get concurrency issues whereby one register detects the change, but the other fails to or sees it one clock cycle later causing the logic to get out of sync with itself.

  • If you are using say a 50MHz clock, it only takes one stray sub-20ns wide spike when the button is pressed for there to be an extra spurious toggle in your edge detector. When you press the button, the output is probably toggling multiple times before finally settling, but you don't see this because it happens so fast (an oscilloscope would probably reveal it).


The metastability/concurrency issues can be resolved easily using a 2-Flop synchroniser to transfer the button input to the clock domain. That way all signals used internally are synchronous and these issues are avoided.

For debouncing you can add a digital filter on the input signal to filter out any changes shorter than some predefined value.

In the example below, I add both the 2-flop synchroniser, and a counter filter. This works by not asserting the pressed signal until it has been continuously high for a set number of clock cycles. It won't de-assert the pressed signal until it has been continuously low for a set number of clock cycles.

You could try with just the 2-flop synchroniser alone to begin with and see if that solves the problem without the need for the filter.

// Example instantiation
wire d1_filt;
debounce_filter #(
 .CNTR_WIDTH(10)
) filter (
 .clock(clk),
 .reset(reset),
 .button(d1),
 .pressed(d1_filt)
);
/*
 * Debounce Filter Module
 */ 
module debounce_filter #(
 parameter CNTR_WIDTH = 10
)(
 input clock,
 input reset,
 
 input button,
 output reg pressed
);
// 2-Flop synchroniser to transfer button signal to clock
// domain and avoid concurrency issues.
reg [1:0] buttonSync;
always @ (posedge clock or posedge reset) begin
 if (reset) begin
 buttonSync <= 2'b0;
 end else begin
 buttonSync <= {buttonSync[0], button};
 end
end
// Debounce Filter logic
// Requires state be stable for 2**CNTR_WIDTH cycles before
// output state is changed. 
reg stable;
reg [CNTR_WIDTH-1:0] filter;
always @ (posedge clock or posedge reset) begin
 if (reset) begin
 filter <= 1'b0;
 end else if (stable) begin
 // Reset filter timer while stable
 filter <= 1'b0;
 end else begin
 // Otherwise let it run.
 filter <= filter + 1'b1;
 end
end
always @ (posedge clock or posedge reset) begin
 if (reset) begin
 stable <= 1'b0;
 pressed <= 1'b0;
 end else if (stable) begin
 // Button is stable, check if it has changed
 if (buttonSync[1] != pressed) begin
 // An edge is detected, no longer stable.
 stable <= 1'b0;
 end
 end else begin
 // Button might be changing, or it might be noise
 if (buttonSync[1] == pressed) begin
 // Returned to its old state, so assume noise.
 stable <= 1'b1;
 end else if (filter == {(CNTR_WIDTH){1'b1}}) begin
 // Counter reached top, stable for long enough to
 // update the button state.
 pressed <= buttonSync[1];
 stable <= 1'b1;
 end
 end
end
endmodule

The timing errors you are seeing are likely simply because you haven't added a timing constraints file (SDC) to tell TimeQuest the frequency of your clock. By default it assumes a 1GHz clock if not told otherwise. In your simple case it's unlikely to cause operational problems, but it is good to fix anyway.

To fix this, you'll need to create a file with a .sdc extension (e.g. ProjectName.sdc) and add it to your Quartus project. In that file, place as a bare minimum the following (change the "50MHz" to whatever your clock period actually is, and clk to whatever your top level clock pin is called).

create_clock -name clock50 -period "50MHz" [get_ports clk]
derive_pll_clocks
derive_clock_uncertainty
answered Mar 3 at 22:07
\$\endgroup\$
7
  • \$\begingroup\$ I don't understard from what you took that d1 signal (is generating by the swticher) is asynchronous signal becouse reaction on d1 changing occurs by posedge clk. \$\endgroup\$ Commented Mar 4 at 12:54
  • \$\begingroup\$ Also, I don't use a button for generating d1 signal, but use a switcher. \$\endgroup\$ Commented Mar 4 at 12:58
  • 1
    \$\begingroup\$ @VladislavButko switch or button it's still asynchronous. To understanding why: your finger is not controlled by the clock in the FPGA, you can change the switch any time. It's possible for the switch to change state just as the clock edge in the FPGA occurs. If this happens the register is not guaranteed to measure high or low, it could be somewhere in the middle causing metastability which can mess with your design. The 2-Flop synchroniser "traps" this metastable level between two registers to give it time to settle, and this prevents it propagating into the rest of the design. \$\endgroup\$ Commented Mar 4 at 14:38
  • \$\begingroup\$ It's really looks like right reason for the debouncing, because the more simple FPGA project (was called to show the problem is not in debouncing) hadn't clk signal. \$\endgroup\$ Commented Mar 5 at 15:16
  • \$\begingroup\$ Although, I don't kwon how to verify your assumption. Cuz I've just programmed simplest FPGA project with assignment (by clk) of input (generated by switcher) to output (led). But debouncing hasn't been occured. \$\endgroup\$ Commented Mar 5 at 15:45
1
\$\begingroup\$

Since your FPGA results to not match your expected behavior, the first thing to do is to look at the FPGA synthesis report files. You should study them in detail because there may be error or warning messages in the reports indicating problems that would result in undesired behavior. If you don't know where to find the reports, read the FPGA synthesis tool documentation.

There are issues with the Verilog code that you posted into the question. The code does not follow good coding practices for synthesis. The code needs to adhere to specific patterns for the synthesis tool to recognize and map to known logic gates.

For example, the reset logic does not follow a common pattern. From the waveforms and how you named the signal, you have an active-high reset. In that case, your always block should check if (reset); I will post the full code below.

Also, to avoid Verilog simulation race conditions, you must use nonblocking assignments (<=) for all signals in your always block because it represents sequential logic. Currently, you are mixing nonblocking with blocking (=).

Here is a better way to code the logic:

module device(d1, reset, clk, out);
input d1, clk, reset;
output reg out;
reg tmp_d1;
always @(posedge clk) begin
 if (reset) begin
 out <= 0;
 end else begin
 if (tmp_d1 != d1) begin
 out <= ~out;
 end
 end 
end
always @(posedge clk) begin
 if (reset) begin
 tmp_d1 <= 0;
 end else begin
 tmp_d1 <= d1;
 end 
end
endmodule

It is not necessary to use 2 always blocks, but it makes the code easier to understand in my opinion.

You also need to be careful how you drive the design inputs from the testbench. Now that you updated the question with the testbench code, there are potential simulation race conditions.

If you want the d1 and reset inputs to be synchronous to the clock (and I think that is what you want), then these inputs should be driven in a manner similar to how you make assignments to the design signals, namel:

  • @(posedge clk)
  • Using nonblocking assignments

Here is a different version of you testbench:

module tb;
reg d1, rst, clk;
wire out;
device device1 (
 .clk (clk),
 .d1 (d1),
 .reset (rst),
 .out (out)
);
always #10 clk = ~clk;
initial begin
 rst = 1;
 clk = 0;
 d1 = 0;
 repeat (2) @(posedge clk);
 rst <= 0;
 repeat (2) @(posedge clk);
 d1 <= 1;
 repeat (3) @(posedge clk);
 d1 <= 0;
 repeat (2) @(posedge clk);
 d1 <= 1;
 repeat (3) @(posedge clk);
 d1 <= 0;
 repeat (3) @(posedge clk);
 $finish;
end
endmodule

For the design instance, I used connections-by-name instead of by position as in your code. Your code shows the connections in the wrong order.

answered Mar 3 at 12:29
\$\endgroup\$
14
  • \$\begingroup\$ Are synthesis tools really that stupid not to be able to do trivial boolean inversion? Or is there some difference in symantics between if (x) and if(!x)? \$\endgroup\$ Commented Mar 3 at 12:34
  • \$\begingroup\$ @user1937198: I agree with you that for a synchronous reset like in this code, there should be no difference, and maybe some synthesis tools will do the right thing. My advice here is to err on the side of caution, and format the code in a more common, expected way. Since the OP is reporting unexpected synthesis results, this is a likely reason. \$\endgroup\$ Commented Mar 3 at 12:38
  • 2
    \$\begingroup\$ While I agree with all of your suggestions in terms of best practice, I doubt you'll have solved the OPs question - I don't have a verision of Quartus as old as the OPs, but on the version I have both your code and the OPs code result in exactly the same synthesis output: RTL, Post-Map. I'm guessing the op is using a push button for D1 without out debouncing, but I'm not going to access their Google Drive video to find out, hence asking them to add the info to their question. \$\endgroup\$ Commented Mar 3 at 14:25
  • \$\begingroup\$ I mentioned about tb in post as link to Google drive \$\endgroup\$ Commented Mar 3 at 14:40
  • \$\begingroup\$ Won't using two the always blocks lead to Verilog simulation race conditions? You check and assignment tmp variable simultanuesly. \$\endgroup\$ Commented Mar 3 at 14:42

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.