Example: Counters

The Counters example is a simple example that integrates Frontpanel components with HDL.

The FrontPanel Platform interface for this example is shown below

Hardware Description

The hardware for the Counters example has two counters, the okHostInterface, a single Wire In endpoint, three Wire Out endpoints, a Trigger In endpoint, and two Trigger Out endpoints. The hardware also routes to the LEDs on the XEM3001.

Counter #1

The first counter is an 8-bit up counter with enable, synchronous reset, disable, and two triggers. The enable signal is generated by a separate 24-bit counter to make the count progression slower. The Verilog HDL for this counter and its clock divider counter is shown here:

always @(posedge sys_clk) begin
   div1 <= div1 - 1;
   if (div1 == 24'h000000) begin
      div1 <= 24'h400000;
      clk1div <= 1'b1;
   end else begin
      clk1div <= 1'b0;
   end
   
   if (clk1div == 1'b1) begin
      if (reset1 == 1'b1)
         count1 <= 8'h00;
      else if (disable1 == 1'b0)
         count1 <= count1 + 1;
      end

   if (count1 == 8'h00)
      count1eq00 <= 1'b1;
   else
      count1eq00 <= 1'b0;

   if (count1 == 8'h80)
      count1eq80 <= 1'b1;
   else
      count1eq80 <= 1'b0;
endCode language: PHP (php)

From the description, we gather that when RESET1 is asserted, the counter will hold the value 0x00. When DISABLE1 is asserted, the counter holds its current value. Otherwise, the counter will increment each time the clock divider counter expires. We also see that COUNT1EQ00 and COUNT1EQ80 are asserted when the counter is 0x00 or 0x80 respectively.

Note that this counter operates on CLK1 which is mapped to LCLK1 on the PLL.

Counter #2

The second counter operates on CLK2 which is mapped to LCLK2 on the PLL. Using the PLL Configuration Dialog, we will be able to observe the effects of changing the PLL frequencies on the two counters.

The Verilog HDL for this counter and its own divider is listed below. This counter will count up when UP2 is asserted, count down when DOWN2 is asserted, and automatically count up when AUTOCOUNT2 is asserted. Note that UP2 and DOWN2 must be asserted for exactly one CLK2 cycle for the counter to count only one.  This is why we have the Trigger endpoints.

always @(posedge sys_clk) begin
   div2 <= div2 - 1;
   if (div2 == 24'h000000) begin
      div2 <= 24'h100000;
      clk2div <= 1'b1;
   end else begin
      clk2div <= 1'b0;
   end
   
   if (reset2 == 1'b1)
      count2 <= 8'h00;
   else if (up2 == 1'b1)
      count2 <= count2 + 1;
   else if (down2 == 1'b1)
      count2 <= count2 - 1;
   else if ((autocount2 == 1'b1) && (clk2div == 1'b1))
      count2 <= count2 + 1;

   if (count2 == 8'hff)
      count2eqFF <= 1'b1;
   else
      count2eqFF <= 1'b0;
endCode language: PHP (php)

Endpoints

The counters example is implemented natively on both AXI and Classic Frontpanel devices. Below are the AXI address map and Classic endpoint descriptions.

AXI Address Map

AddressnameaccessDESCRIPTION
0x0000_0000CTRLR/W[0] reset1, [1] disable1, [2] autocount2, [31:3] Unused
0x0000_0020COUNT1R[7:0] counter1 value
0x0000_0024COUNT2R[7:0] counter2 value
0x0000_0040TRIG_CTRLW[0] reset2, [1] up2, [2] down2
0x0000_0060TRIG_STATUSR[0] count1==0x00, [1] count1==0x80, [2] count2==0xFF

Classic Endpoints

This example uses several endpoints to provide controllable inputs to the hardware and observable outputs to FrontPanel.  To reduce the number of endpoints, we have chosen to share them among the counters.

Wire In (0x00)

The only Wire In endpoint is used to carry the RESET1, DISABLE1, and AUTOCOUNT2 signals. These are wires because we want them to have a static state rather than one-shot signals.

SIGNALBIT(S)DESCRIPTION
RESET10When asserted, Counter #1 holds the value 0x00 and does not count.
DISABLE11When asserted, Counter #2 holds its value and does not count.
AUTOCOUNT22Configures counter #2 to autocount.
Unused15:3

Trigger In (0x40)

The only Trigger In endpoint is used for the Counter #2 inputs. These are triggers because we want single events (one-shots) to occur, such as a count-up event.

Note that RESET2 behaves the same as RESET1 but we want to have RESET2 behave as a one-shot event so that the user cannot hold RESET2 asserted. Therefore, we attach this one to a Trigger.

SIGNALBIT(S)DESCRIPTION
RESET20When asserted, Counter #2 resets to 0x00 and does not count.
UP21When asserted, Counter #2 counts up.
DOWN22When asserted. Counter #2 counts down.
Unused15:3

Wire Out (0x20, 0x21, and 0x22)

These wires provide observables for FrontPanel. They are connected as follows:

ENDPOINTSIGNALDESCRIPTION
Wire Out 0x20COUNT1[7:0]Counter #1 value.
Wire Out 0x21COUNT2[7:0]Counter #2 value.
Wire Out 0x22BUTTON[3:0]The lower four bits of this wire bundle contain the status of the on-board pushbuttons.  If a button is pressed, the corresponding wire will be asserted.

Trigger Out (0x60)

This Trigger Out endpoint corresponds to the Counter #1 value. These are triggers because we want these events to update the FrontPanel application.

SIGNALBIT(S)DESCRIPTION
COUNT1EQ000When asserted, Counter #1 value is equal to 0x00
COUTN1EQ801When asserted, Counter #1 value is equal to 0x80
Unused15:2

Trigger Out (0x61)

This Trigger Out endpoint corresponds to the Counter #2 value.

SIGNALBIT(S)DESCRIPTION
COUNT2EQFF0When asserted, Counter #2 value is equal to 0xFF
Unused15:1

MCP Description

The Counters example also serves as an example on integrating the Frontpanel MCP. The MCP surface is six tools across the two counters. These tools mirror the control surface on the GUI.

ToolKindDescription
counter1_resetactionPulse the reset line for counter 1, returning it to zero.
counter1_set_enabledwriteEnable or disable counting on counter 1. When disabled the counter holds its current value.
counter1_readreadRead the current value of counter 1.
counter2_stepactionStep counter 2 by one in the chosen direction ("up" or "down").
counter2_set_auto_countwriteToggle counter 2’s autonomous-count mode.
counter2_readreadRead the current value of counter 2.

Example Session — Claude Desktop

A short conversational interaction with Counters.

User: What are the counters reading?

  → counter1_read()           → 4218
  → counter2_read()           → 17

AI: Counter 1 is at 4218 and counter 2 is at 17.

User: Stop counter 1 and reset it, then step counter 2 down by five.

  → counter1_set_enabled({ enabled: false })
  → counter1_reset()
  → counter2_step({ direction: "down" })   × 5
  → counter1_read()           → 0
  → counter2_read()           → 12

AI: Counter 1 is held at 0; counter 2 is now at 12.Code language: plaintext (plaintext)

Each tool call in the trace also produces a UI-side update — the user watches the on-screen counter values change in real time as the AI works through the request.