Counting Debounced Events

Counting...

The questions below are due on Wednesday September 18, 2024; 11:59:00 PM.
 
You are not logged in.

Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

Getting Started

Just like last time, create a new project folder. Repeat the file structure we used in Lab 01, except call this one lab02 or something relevant, with the following folders:

  • hdl: to hold all your Verilog files that will be synthesized (built on the FPGA)
  • xdc: to hold the XDC file for mapping and activating pins
  • sim: to hold all Python files that are for simulating/testbenching
  • obj: to hold all output files from your Vivado builds

Take the generic build tcl script and place it in the root of your lab folder. A starting version of the the xdc file for this lab should be placed in your xdc folder.

Create a top level file called top_level.sv in hdl. Use the following starting skeleton for your top_level.sv:

`default_nettype none // prevents system from inferring an undeclared logic (good practice)

module top_level(
  input wire clk_100mhz, //100 MHz onboard clock
  input wire [15:0] sw, //all 16 input slide switches
  input wire [3:0] btn, //all four momentary button switches
  output logic [15:0] led, //16 green output LEDs (located right above switches)
  output logic [2:0] rgb0, //RGB channels of RGB LED0
  output logic [2:0] rgb1, //RGB channels of RGB LED1
  output logic [3:0] ss0_an,//anode control for upper four digits of seven-seg display
  output logic [3:0] ss1_an,//anode control for lower four digits of seven-seg display
  output logic [6:0] ss0_c, //cathode controls for the segments of upper four digits
  output logic [6:0] ss1_c //cathode controls for the segments of lower four digits
  );

  //shut up those rgb LEDs for now (active high):
  assign rgb1 = 0; //set to 0.
  assign rgb0 = 0; //set to 0. Change later!!

  //have btnd control system reset
  logic sys_rst;
  assign sys_rst = btn[0];

  //how many button presses have we seen so far?
  //wire this up to the LED display
  logic [15:0] btn_count; //use me to keep track of counting
  assign led = btn_count;

  //downstream/display variables:
  logic [31:0] val_to_display; //either the spi data or the btn_count data (default)
  logic [6:0] ss_c; //used to grab output cathode signal for 7s leds

  // debouncer for the button. we wrote this in lecture together.
  //TODO: make a variable for the debounced
  //button output, and feed it into your edge detector

  debouncer btn1_db(.clk_in(clk_100mhz),
                    .rst_in(sys_rst),
                    .dirty_in(btn[1]),
                    .clean_out(/* your variable here */));

  // this signal should go high for one cycle on the ..
  //rising edge of the (debounced) button output
  logic btn_pulse;

  //TODO: write your edge detector for part 1 of the lab here!

  //the button-press counter.
  //TODO: finish this during part 1 of the lab
  evt_counter msc(.clk_in(clk_100mhz),
                  .rst_in(sys_rst),
                  .evt_in(btn_pulse),
                  .count_out(btn_count));

  //for starters just display button count:
  assign val_to_display = btn_count;

  //uncomment seven segment module for part 2!
  //
  //seven_segment_controller mssc(.clk_in(clk_100mhz),
  //                              .rst_in(sys_rst),
  //                              .val_in(val_to_display),
  //                              .cat_out(ss_c),
  //                              .an_out({ss0_an, ss1_an}));
  //

  assign {ss0_an, ss1_an} = 8'h00; // Remove this for part 2!

  assign ss0_c = ss_c; //control upper four digit's cathodes!
  assign ss1_c = ss_c; //same as above but for lower four digits!

endmodule // top_level

`default_nettype wire

Make sure to uncomment the appropriate lines in the top_level.xdc corresponding with the inputs and outputs of top_level.sv.

As a reminder/refersher, the pins above are the following:

  • clk_100mhz: Our clock; make sure to uncomment both related lines to this at the top of the xdc
  • led[15:0]: All sixteen green LEDs (above the switches)
  • All 8 an anode pins of the seven-segment display, which are split into two "banks":
    • ss0_an[3:0] which control the on/off nature of each of the four upper digits (active low)
    • ss1_an[3:0] which controls the on/off nature of each of the four lower digits (active low)
  • The cathode pins of the seven-segment display, which are split into two "banks":
    • ss0_c[6:0] which controls the segments of all four upper digits (active low)
    • ss1_c[6:0] which controls the segments of all four lower digits (active low)
  • btn[1]: This button will be debounced and counted
  • btn[0]: We'll use this button as a global reset
  • btn[2]: We'll eventually use this to show either the button count or the transmit/receive pair of data
  • rgb1[2:0] and rgb0[2:0]: Which we set to 0 since otherwise the circuit that drives them floats high and turns them on and they are annoyingly bright.

Inside your hdl folder you should also create the following files:

  • debouncer.sv: A module we gave you after we built it in lecture (find it again here!)
  • evt_counter.sv: A module that will count events (and be resettable). Can be blank for the moment; a skeleton is provided in the text below.
  • seven_segment_controller.sv: A module that will control your entire 8 digit, 7-segment display. Can be blank for the moment; a skeleton is provided in part two of the lab.
  • spi_con.sv: Your SPI (Serial Peripheral Interface) controller module from the exercise this week (to be written after checkoff 2 so can also be blank for the moment).

Button Counting

You'll now be implementing this design:

Button Counter Overall. debouncer should already be written, edge_detector doesn't actually have to be a whole module. Feel free to place that logic in the top_level module directly. evt_counter needs to be completed by you.

Parts of this signal pipeline are already in place in the source provided, but others are missing. In building this pipeline, we'll work backwards: First we'll make a counter, then we'll handle edge-detection (i.e., emit a pulse when the button is first pressed), and finally we'll drop in your debouncer module.

Adding a Counter

Last week you wrote a counting module that had a maximum count that was user-specifiable. There's lots of other variations on counters that you'll come across...it is a very common thing you'll need to build. One variation of a counter that is particularly useful in designs is something that tallies the occurences of an event. We'll be doing that for the first part of lab by creating a module called evt_counter that has three inputs:

  • clk_in: The system clock
  • rst_in: A reset for the system
  • evt_in: The event to be counted; when evt_in is HIGH on a rising edge of clk_in, the counter should increment by one
  • count_out[15:0]: The output of the counter module; note that this value is 16 bits, so it should be able to count from 16'b0000_0000_0000_0000 to 16'b1111_1111_1111_1111

In addition:

  • If rst_in is asserted, count_out is reset to zero synchronously (on the rising edge of the clock). It should stay set to zero while rst_in is asserted.
  • If rst_in is not asserted and evt_in is high on a rising clock edge, the count should be increased by 1.
  • Don't worry about overflowing the counter.

We provide a starting skeleton below. Your job is to finish it based on the spec above.

`default_nettype none
module evt_counter
  ( input wire          clk_in,
    input wire          rst_in,
    input wire          evt_in,
    output logic[15:0]  count_out
  );

  always_ff @(posedge clk_in) begin
    if (rst_in) begin
      count_out <= 16'b0;
    end else begin
      /* your code here */
    end
  end
endmodule
`default_nettype wire

Since there's a lot of moving parts going into this lab's checkoff 1, it would be a good idea to test and verify the behavior of this module prior to integration. We provide a testbench for this module here: test_evt_counter.py.

Run this testbench like you did last week. There are some assertions in this code that do some checks. In addition, you studying the output wave files is important.

screenshot of gtkwave

What a waveform might look like after you plot some stuff. To plot the current value of something in your code, go into the dropdown menu on the top left and select the module you're interested in. From there, a list of your signals will show up in the bottom left - drag these into the Signal box to see them graphed over time.

In simulation, unless things get explicitly set to a value, they will stay undefined! These undefined values will propagate through the simulation. Notice how in your waveform, count variable is XXXX (and red) at the very beginning of the trace. This means that the output has yet to be driven! If you started using our evt_counter skeleton, you'll notice that only on the rising edge of rst_in and clk_in does count become 0. In reality, when synthesized and placed on a device, even if signals are not initialized, they will generally start at 0. Regardless though, it's good practice to get rid of these demonic 'XXX's when possible (:

Once you've inspected your testbench waveform output and it seems to be working and counting and resetting where appropriate, move onto to the next part.

Edge Detector

Debouncing the switch "cleans" up the switch signal so upon push or release, it only transitions exactly once. However with the way our evt_counter module is written, if we feed the debounced input directly into the counter, one push may result in literally hundreds of thousands of "events" depending how long we push the button for (question: why? Make sure that makes sense since we will ask you about it during checkoff). We instead should only count the event of a push as it happens.

an old timey button labeled 'push to engage'

Push to Engage

In other words, we need to create a rising-edge signal that lasts for only one clock cycle whenever the button is pressed. A simple way of generating an edge signal is to remember the previous value of a signal, and use that to look for a rising edge. In your top_level module, write some code that looks for the rising edge of the output of debounce, and goes high when that happens. Because this code must happen over time it must be getting evaluated in a always_ff block.

Debouncer

The final piece to add is right at the beginning of the signal pipeline - the debouncer, which we wrote together in class. This module should take in the button signal which may be "bouncy" and "dirty" and clean it up so that there are no unfortunate back-forth-back-forth signal transitions that may arise. Integrate this module into your system.

Make sure the output of your counter is tied to the sixteen LEDs, and build + show that it works. As a reminder, the command to flash your output bitstream (let's say it's obj/final.bit) is:

openFPGALoader -b arty_s7_50 obj/final.bit

At the link below is a video of the thing working as expected. Make sure your system counts btn[1] pushes robustly and can be reset by pushing btn[0].

Checkoff 1:
Show us your event counter working in simulation (in the waveform viewer) and on the FPGA. Be prepared to answer some questions. Note, you're also welcome to move onto the next section, and do checkoff 1 and checkoff 2 at the same time since they build on eachother. Just make sure to ask for Checkoff 1 and Checkoff 2 via the website when you get there since we can't give you a checkoff without you asking for it via your own web page.

All Done? Move on and get started on Part Two of this week's assignments.