Skip to main content Link Search Menu Expand Document (external link)

50.002 Computation Structures
Information Systems Technology and Design
Singapore University of Technology and Design

Clocks

This document is written to guide you with generating new clock signals for your project should it fails to meet timing specifications. If your project works in simulation but behaves erratically on physical FPGA, it is likely that you have timing issues.

Motivation

The Alchitry Au FPGA board runs at a default system clock of 100 MHz, which is generated by an onboard oscillator. It is a crystal oscillator module soldered onto the board that provides a clean 100 MHz square wave clock signal. However, sometimes your design might fail to meet the timing constraints, e.g: ALU multiply takes too long and violates the t2 constraint:

To make your project works reliably, you need to add extra logic to slow your FSM (let it loop for several cycles before capturing its result) or to slow down the clock. The latter is more straightforward but cumbersome to do: it requires you to access Vivado and generate clock with another frequency.

Changing the clock frequency in the alchitry.acf constraint file does not change the default clk signal going to your alchitry_top. The constraint file is for telling the tools what the frequency is for timing analysis only, not to control the frequency.

Method 1: Delay the FSM

If you need more than 10 ns (since the onboard clock is fixed at 100Mhz) of time per state, for instance: your ALU’s tpd for CMPEQ or MUL is taking too long, you need to loop a few times in the same state before capturing the output of your ALU. Otherwise, your ALU won’t be abl to output the correct value in the same clock cycle.

The following state won’t behave as expected. We won’t store the right CMPEQ result into R6 in this state for example:

    States.CHECK_SCORE:    
        alufn = b110011 // CMPEQ
        regfile_ra1 = d1 // score dff
        asel = b00 // rd1 to alu 
        bsel = b01  // constant 10
        regfile_we = 1 
        regfile_wa = d6 // temp dff
        game_fsm.d = States.BRANCH_SCORE                    

You should have fixed your ALU to be able to output a stable result within 1 period of FSM clock (remove any sequential component). However, if you need to have any sequential (like division) or complex component (very long tpd) in the ALU, then you will need to wait several cycles in this state before advancing to the next state, e.g BRANCH_SCORE.

You can do this by declaring a temp dff in your control unit / fsm module:

    dff state_delay[4](.clk(clk), .rst(rst), #INIT(0))

And then utilise this in the state to introduce delay:

    States.CHECK_SCORE:    
        // increment state_delay 
        state_delay.d = state_delay.q + 1
        alufn = b110011 // CMPEQ
        regfile_ra1 = d1 // score dff
        asel = b00 // rd1 to alu 
        bsel = b01  // constant 10
        regfile_wa = d6 // temp dff

        if (&state_delay.q){
            regfile_we = 1  // write only once at the end of the delay cycle
            state_delay.d = 0 // reset state_delay
            game_fsm.d = States.BRANCH_SCORE // go to next state              
        }

This way, the FSM maintains the same control signal for 16 onboard clock cycles (160ns) and then only capture the output at the the 16th cycle, while transitioning to the next state. This should give enough time for the ALU output to settle. You should adjust the size of the dff accordingly.

You can find out how long you need to wait for the output of the ALU to stabilize by changing the clock frequency in alchitry.acf file.

Method 2: Generate New Clock Signal using Clock Wiz in Vivado IP Catalog

In this method, we will use Vivado to use a MMCM (Mixed-Mode Clock Manager) to generate a custom clock signal from the default 100 MHz input clock.

MMCM

An MMCM (Mixed-Mode Clock Manager) is a clock management block in Xilinx FPGAs that generates derived clocks with configurable frequency, phase, and duty cycle.

Open Vivado IP Catalog from the component menu:

Wait awhile, and you will see Vivado launched:

Search FPGA Features and Design, choose Clocking, then double click Clocking Wizard to launch:

The Clocking Wizard window will appear. At the Clocking Options tab, choose MMCM. The MMCM module will be expecting a 100MHz input signal, and is coming from the Single-ended clock capable pin, N14, from your Alchitry Au.

Then, at the Output Clocks tab, change the Requested Frequency to be the one you want, e.g: 10MHz. You might also want to change the component name to follow the frequency so you remember which component is producing what. For instance, clk_wiz_10 for 10MHz clock.

At the bottom, delete the optional reset and locked:

Finally, click OK then Generate:

It will take some time.

Once done, you can close Vivado. When you return to Alchitry Labs, you will see your new clock created:

You can then use it like any other module:

    clk_wiz_10 slow_clock(.clk_in1(clk), .reset(rst))
    
    .clk(slow_clock.clk_out1){
        dff slow_counter[32](.rst(rst))
    }

Test

This alchitry_top.luc file blinks two LEDs:

module alchitry_top (
    input clk,              // 100MHz clock
    input rst_n,            // reset button (active low)
    output led[8],          // 8 user controllable LEDs
    input usb_rx,           // USB->Serial input
    output usb_tx,          // USB->Serial output
    output io_led[3][8],    // LEDs on IO Shield
    output io_segment[8],   // 7-segment LEDs on IO Shield
    output io_select[4],    // Digit select on IO Shield
    input io_button[5],     // 5 buttons on IO Shield
    input io_dip[3][8]      // DIP switches on IO Shield
) {

    sig rst                 // reset signal
    clk_wiz_10 slow_clock(.clk_in1(clk))
    
    // dff using slow 10MHz clock
    dff slow_counter[32](.rst(rst),.clk(slow_clock.clk_out1))
    dff led_state_slow(.rst(rst), .clk(slow_clock.clk_out1), #INIT(0))
    
    // regular 100MHz clock
    .clk(clk) {
        // The reset conditioner is used to synchronize the reset signal to the FPGA
        // clock. This ensures the entire FPGA comes out of reset at the same time.
        reset_conditioner reset_cond
        dff counter[32](.rst(rst), #INIT(0))
        dff led_state(.rst(rst), #INIT(0))
    }
    
    always {
        reset_cond.in = ~rst_n  // input raw inverted reset signal
        rst = reset_cond.out    // conditioned reset
        
        led = 8h00              // turn LEDs off
        
        usb_tx = usb_rx         // loop serial port
        
        io_led = 3x{{8h00}}
        io_segment = 8hff
        io_select = 4hf
        
        led_state.d = led_state.q
        led_state_slow.d = led_state_slow.q
        
        counter.d = counter.q + 1 
        slow_counter.d = slow_counter.q + 1
        
        if (counter.q == d10_000_000){
            led_state.d = ~led_state.q
            counter.d = 0
        }
        
        if (slow_counter.q == d10_000_000){
            led_state_slow.d = ~led_state_slow.q
            slow_counter.d = 0
        }
        
        io_led[0][0] = led_state.q
        io_led[1][0] = led_state_slow.q
        io_led[2] = counter.q
        led = slow_counter.q
        
    }
}

The two LEDs are:

        io_led[0][0] = led_state.q // will blink 10x faster than the other LED
        io_led[1][0] = led_state_slow.q

Summary

If your project works in simulation but behaves erratically on physical FPGA, it is likely that you have timing issues.

Whenever your project fails to meet timing constraints, remember to:

  1. Modify alchitry.acf to let Vivado do the timing analysis properly with the new clock frequency and ensure that it meets the timing constraints
  2. Create a new clock using Vivado Clock Wiz (better choice), or manually delay certain FSM states where you suspect takes more than 10ns to complete (works, but more of a guessing game)