📈
Ween's Lab
UdemyYouTubeTikTok
  • Welcome
  • 📻FPGA Tutorials
    • FPGA Boards: Getting Started
      • Getting Started with PYNQ on Kria KV260 Vision AI Starter Kit
      • Getting Started with PYNQ on Red Pitaya STEMlab 125-14
      • Getting Started with PYNQ on ZYBO
    • FPGA Ethernet Tutorial
      • FPGA Tutorial Ethernet 1: Simple TCP Server
    • PYNQ FPGA Tutorial 101
      • Part 0: Introduction
      • Part 1: GPIO
      • Part 2: Custom IP
      • Part 3: Memory
      • Part 4: ANN Processor
    • PYNQ FPGA Tutorial 102
      • Part 0: Introduction
      • Part 1: Memory Mapped
      • Part 2: Direct Memory Access
      • Part 3: AXI-Lite Multiplier
      • Part 4: AXI-Stream Multiplier with DMA
      • Part 5: AXI-Lite GCD
      • Part 6: AXI-Stream GCD with DMA
      • Part 7: Access to DDR from PL
    • ZYNQ FPGA Tutorial
      • Part 1: Gate-Level Combinational Circuit
      • Part 2: RT-Level Combinational Circuit
      • Part 3: Regular Sequential Circuit
      • Part 4: FSM Sequential Circuit
      • Part 5: ZYNQ Architecture
      • Part 6: ARM CPU and FPGA Module
      • Part 7: FPGA Memory
      • Part 8: Hardware Accelerator for Neural Networks
    • ZYNQ FPGA Linux Kernel Module
      • Cross Compiling Kernel, Kernel Module, and User Program for PYNQ
      • Configure PL to PS Interrupt in Kernel Module
      • Configure AXI DMA in Kernel Module
  • 📟Proyek Arduino
    • Kumpulan Proyek
      • Rangkaian LED
      • LED Berkedip Nyala Api
      • LED Chaser
      • LED Binary Counter
      • OLED 128x4 Bitcoin Ticker
      • Rangkaian Button
      • Button Multifungsi
      • Button Interrupt
      • Button Debouncing
    • Pelatihan Mikrokontroler Arduino ESP32
      • Bab 1 Pengenalan Mikrokontroler
      • Bab 2 Pengenalan Arduino
      • Bab 3 Pengenalan Bahasa C
      • Bab 4 Digital Output
      • Bab 5 Digital Input
      • Bab 6 Serial Communication
      • Bab 7 Analog-to-Digital Conversion
      • Bab 8 Interrupt
      • Bab 9 Timer
      • Bab 10 Pulse-Width Modulation
      • Bab 11 SPI Communication
      • Bab 12 I2C Communication
  • 💰Finance
    • Coding for Finance
      • Build a Bitcoin Price Alert with Google Cloud and Telegram
      • Build a Bitcoin Ticker with ESP32 and Arduino
      • Stock Price Forecasting with LSTM
    • Trading dan Investasi
      • Istilah Ekonomi, Keuangan, Bisnis, Trading, dan Investasi
      • Jalan Menuju Financial Abundance
      • Memahami Korelasi Emas, Oil, Dollar, BTC, Bonds, dan Saham
      • Mindset Trading dan Investasi
      • Rangkuman Buku: Rahasia Analisis Fundamental Saham
      • Rangkuman Buku: The Psychology of Money
      • Rangkuman Kuliah: Introduction to Adaptive Markets
      • Rumus Menjadi Orang Kaya
  • 📝Life
    • Life Quotes
Powered by GitBook
On this page
  • Objective
  • Source Code
  • References
  • 1. Synchronous Design Methodology
  • 1.1. D Flip-Flop and Register
  • 1.2. Synchronous System
  • 1.3. Timing Parameters
  • 1.4. Maximal Operating Frequency
  • 1.5. Timing Constraint in Vivado
  • 2. Design Examples
  • 2.1. Register
  • 2.2. Counter
  • 2.3. Counter as FSM
  • 3. Conclusion
  1. FPGA Tutorials
  2. ZYNQ FPGA Tutorial

Part 3: Regular Sequential Circuit

PreviousPart 2: RT-Level Combinational CircuitNextPart 4: FSM Sequential Circuit

Last updated 7 months ago

Objective

This tutorial contains information about sequential circuit and several design examples of sequential circuit.

Source Code

This repository contains all of the code required in order to follow this tutorial.

References

1. Synchronous Design Methodology

A sequential circuit is a circuit with memory, which forms the internal state of the circuit. Unlike a combinational circuit, in which the output is a function of input only, the output of a sequential circuit is a function of the input and the internal state.

The synchronous design methodology is the most commonly used practice in designing a sequential circuit. In this methodology, all storage elements are controlled (i.e., synchronized) by a global clock signal and the data is sampled and stored at the rising or falling edge of the clock signal.

1.1. D Flip-Flop and Register

The most basic storage component in a sequential circuit is a D-type flip-flop (D FF) as shown in the following figure.

This is the truth table for D FF. The next q value (q*) is equal to input d at the rising edge of the clock and when the reset is 1. Note that this rst_n signal is active-low, so the D FF is reset to 0 when the rst_n is 0.

clk
rst_n
q*

0

-

q

1

-

q

↑

0

0

↑

1

d

This is the implementation of D FF in Verilog. In a sequential circuit, we use non-blocking assignments (<=) inside the always block.

module dff
    (
	input wire clk,
	input wire rst_n,
	input wire d,
	output reg q
    );

    always @(posedge clk)
	if (!rst_n)
	    q <= 0;
	else
	    q <= d;

endmodule

A register is a collection of D FFs that are controlled by the same clock and reset signals. The Verilog code of the register is the same as D FF, but it has more than 1 bit of input and output. This example shows an 8-bit register.

module reg_8bit
    (
        input wire       clk,
        input wire       rst_n,
        input wire [7:0] d,
        output reg [7:0] q
    );

    always @(posedge clk)
        if (!rst_n)
            q <= 0;
        else
            q <= d;

endmodule

1.2. Synchronous System

The following figure shows a block diagram of a synchronous system. It consists of the following parts:

  • State register: a collection of D FFs controlled by the same clock signal (state_reg).

  • Next-state logic: combinational logic that uses the external input and internal state (i.e., the output of register) to determine the new value of the register (state_next).

  • Output logic: combinational logic that generates the output signal.

Synchronous system are classified into three groups based on their next-state logic properties:

  • Regular sequential circuit: the state transitions in the circuit exhibit a "regular" pattern, as in a counter or shift register. The next-state logic is constructed primarily by a predesigned, "regular" component, such as an incrementor or shifter.

  • Finite state machine (FSM): the state transitions in the circuit do not exhibit a simple, repetitive pattern. The next-state logic is constructed by "random logic" and synthesized from scratch. It should be called a random sequential circuit, but is commonly known as an FSM (finite state machine).

  • Finite state machine with data path (FSMD): the circuit consists of a regular sequential circuit and an FSM. The two parts are known as a data path and a control path, and the complete circuit is known as an FSMD (FSM with data path).

In this tutorial, we focus on the regular sequential circuit. FSM and FSMD will be the next part's topic.

1.3. Timing Parameters

1.4. Maximal Operating Frequency

and the maximal clock rate is the reciprocal:

1.5. Timing Constraint in Vivado

We can specify the desired operating frequency as a synthesis constraint or derived from IP core settings, and the synthesis software will try to obtain a circuit to satisfy this requirement. After synthesis, we can check the relevant timing information to see whether the imposed constraints are met or not.

The synthesis result will also show the worst negative slack (WNS) for setup time. From this slack time, we can calculate the maximal frequency by:

2. Design Examples

2.1. Register

The following figure shows a register block diagram. It consists of a state register and next-state logic. This register has an active-low reset (rst_n) and a clear signal (clr). When either one of these signals is active, the register value will be set to 0. The enable signal controls the input either from the previous register value (q) or the new input (d). The value of the register will be updated only if the enable signal is 1.

This is the implementation of a register in Verilog. We use parameterized code to specify the register width.

register.v
module register
    #( 
        parameter WIDTH = 16
    )
    (
        input wire                    clk,
        input wire                    rst_n,
        input wire                    en,
        input wire                    clr,
        input wire signed [WIDTH-1:0] d,
        output reg signed [WIDTH-1:0] q
    );
    
    always @(posedge clk)
    begin
        if (!rst_n || clr)
        begin
            q <= 0;
        end
        else if (en)
        begin
            q <= d;
        end
    end
    
endmodule

The following code is the testbench for the register.

register_tb.v
`timescale 1ns / 1ps

module register_tb();
    localparam T = 10;
    
    reg clk, rst_n, en, clr;
    reg [7:0] d;
    wire [7:0] q;
    
    register #(8)
    dut(.clk(clk), .rst_n(rst_n), .en(en), .clr(clr), .d(d), .q(q));
    
    always
    begin
        clk = 0;
        #(T/2);
        clk = 1;
        #(T/2);
    end
    
    initial
    begin
        en = 1; clr = 0; d = 0;
        
        rst_n = 0; #T;
        rst_n = 1; #T;
        
        d = 8'd8; #T;
        d = 8'd16; #T;
        
        clr = 1; #T;
    end
endmodule

This is the simulation result of the register. The d value is loaded to the q at the rising edge of the clock.

2.2. Counter

The following figure shows a counter block diagram. It consists of a state register and next-state logic. This counter starts counting up when the start signal is 1. Then, it will count up from 0 to 5. After that, it resets back to 0 and waits for the start signal again.

This is the implementation of the counter in Verilog.

counter.v
module counter
    (
        input wire        clk,
        input wire        rst_n,
        input wire        clr,
        input wire        start,
        output wire [3:0] q
    );
    
    reg [3:0] cnt_reg;

    always @(posedge clk)
    begin
        if (!rst_n || clr)
        begin
            cnt_reg <= 0;
        end
        else if (start)
        begin
            cnt_reg <= cnt_reg + 1;
        end
        else if (cnt_reg >= 1 && cnt_reg <= 4)
        begin
            cnt_reg <= cnt_reg + 1;
        end
        else if (cnt_reg >= 5)
        begin
            cnt_reg <= 0;
        end
    end
    
    assign q = cnt_reg;

endmodule

The following code is the testbench for the counter.

counter_tb.v
`timescale 1ns / 1ps

module counter_tb();
    localparam T = 10;
    
    reg clk, rst_n, clr, start;
    wire [7:0] q;
    
    counter dut(.clk(clk), .rst_n(rst_n), .clr(clr), .start(start), .q(q));
    
    always
    begin
        clk = 0;
        #(T/2);
        clk = 1;
        #(T/2);
    end
    
    initial
    begin
        clr = 0;
        start = 0;
        
        rst_n = 0; #T;
        rst_n = 1; #T;
        
        start = 1; #T;
        start = 0;
    end
endmodule

This is the simulation result of the counter module.

Then, we can add the counter module to the Vivado block design, add a virtual input/output (VIO) IP, and also add an integrated logic analyzer (ILA) IP. The ILA IP functions as an integrated logic analyzer so that we can observe the waveform that actually happens inside the FPGA.

This is the constraints for this block design. We only use clock signal that has a period of 8.0 ns (125 Mhz).

#Clock signal
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L11P_T1_SRCC_35 Sch=sysclk
create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports { clk }];

The following figure shows the synthesis result.

We can view the detailed resource utilization. The counter module only uses 5 LUTs and 4 registers. Most of the resources are for the VIO and ILA IP.

From the result, we can see that the timing requirements are met for this 125 MHz clock. There is setup WNS value which is 1.730 ns. This means that we could have asked for a clock period that is shorter by 1.730 ns, and it would still be OK. So the requested clock period could have been 8.0 - 1.730 ns = 6.270 ns, which is about 159 MHz.

The following figure shows how we can observe the waveform of the counter module using the ILA interface in Vivado (after programming the FPGA). First, we have to set up the trigger signal for this ILA, which is the start signal. Then, on the VIO, we toggle the start to 1.

2.3. Counter as FSM

Suppose we want to create a controller for a system that consists of 1 RAM for data input, 1 PE, and 1 RAM for data output. The RAM has two signals that need to be controlled, which are enable (en_rd, en_wr) and address (addr_rd, addr_wr) signals. The PE has one signal that needs to be controlled, which is enable (en_pe) signal. The requirements of the timing diagram are shown below.

In addition to that, there are status signals, which are ready and done_tick. The counter starts when the start signal is 1. We can implement these requirements using a counter module. The following figure shows the circuit implemented for that purpose.

The circuit consists of three counters. One for FSM, and the others for address reading and writing. Then, from the counter FSM output, we can build the output logic. The logic uses comparators and 2-to-1 multiplexers. The logic is based on the current counter FSM value, which starts from 1 to 7. Based on that counter value and output logic condition, the output signals are generated.

This is the implementation of the counter as FSM in Verilog. In this module, we create both combinational and sequential circuits. We use both continuous and procedural assignments.

counter_fsm.v
module counter_fsm
    (
        input wire        clk,
        input wire        rst_n,
        input wire        clr,
        input wire        start,
        output wire       done_tick,
        output wire       ready,
        output wire       en_rd,
        output wire [3:0] addr_rd,
        output wire       en_pe,
        output wire       en_wr,
        output wire [3:0] addr_wr
    );
    
    reg [3:0] cnt_fsm_reg;
    reg [3:0] cnt_addr_rd_reg;
    reg [3:0] cnt_addr_wr_reg;
    wire start_cnt_addr_rd;
    wire start_cnt_addr_wr;
    
    // Main counter as FSM
    always @(posedge clk)
    begin
        if (!rst_n || clr)
        begin
            cnt_fsm_reg <= 0;
        end
        else if (start)
        begin
            cnt_fsm_reg <= cnt_fsm_reg + 1;
        end
        else if (cnt_fsm_reg >= 1 && cnt_fsm_reg <= 6)
        begin
            cnt_fsm_reg <= cnt_fsm_reg + 1;
        end
        else if (cnt_fsm_reg >= 7)
        begin
            cnt_fsm_reg <= 0;
        end
    end
    
    // Control to start address read counter
    assign start_cnt_addr_rd = (cnt_fsm_reg == 1) ? 1 : 0;
    // Control to enable read
    assign en_rd = ((cnt_fsm_reg >= 1) & (cnt_fsm_reg <= 4)) ? 1 : 0;
    // Control to enable pe
    assign en_pe = ((cnt_fsm_reg >= 2) & (cnt_fsm_reg <= 5)) ? 1 : 0;
    // Control to start address write counter
    assign start_cnt_addr_wr = (cnt_fsm_reg == 3) ? 1 : 0;
    // Control to enable write
    assign en_wr = ((cnt_fsm_reg >= 3) & (cnt_fsm_reg <= 6)) ? 1 : 0;
    // Status done
    assign done_tick = (cnt_fsm_reg == 7) ? 1 : 0;
    // Status ready
    assign ready = (cnt_fsm_reg == 0) ? 1 : 0;
        
    // Additional counter for address read
    always @(posedge clk)
    begin
        if (!rst_n)
        begin
            cnt_addr_rd_reg <= 0;
        end
        else if (start_cnt_addr_rd)
        begin
            cnt_addr_rd_reg <= cnt_addr_rd_reg + 1;
        end
        else if (cnt_addr_rd_reg >= 1 && cnt_addr_rd_reg <= 2)
        begin
            cnt_addr_rd_reg <= cnt_addr_rd_reg + 1;
        end
        else if (cnt_addr_rd_reg >= 3)
        begin
            cnt_addr_rd_reg <= 0;
        end
    end
    // Address read output
    assign addr_rd = cnt_addr_rd_reg;
        
    // Additional counter for address write
    always @(posedge clk)
    begin
        if (!rst_n)
        begin
            cnt_addr_wr_reg <= 0;
        end
        else if (start_cnt_addr_wr)
        begin
            cnt_addr_wr_reg <= cnt_addr_wr_reg + 1;
        end
        else if (cnt_addr_wr_reg >= 1 && cnt_addr_wr_reg <= 2)
        begin
            cnt_addr_wr_reg <= cnt_addr_wr_reg + 1;
        end
        else if (cnt_addr_wr_reg >= 3)
        begin
            cnt_addr_wr_reg <= 0;
        end
    end
    // Address write output
    assign addr_wr = cnt_addr_wr_reg;
    
endmodule

The following code is the testbench for the counter as FSM.

counter_fsm_tb.v
`timescale 1ns / 1ps

module counter_fsm_tb();
    localparam T = 10;
    
    reg clk, rst_n, clr, start;
    wire en_rd, en_pe, en_wr;
    wire [3:0] addr_rd, addr_wr;
    
    counter_fsm dut (.clk(clk), .rst_n(rst_n), .clr(clr), .start(start),
        .en_rd(en_rd), .addr_rd(addr_rd), .en_pe(en_pe), .en_wr(en_wr),
        .addr_wr(addr_wr));
    
    always
    begin
        clk = 0;
        #(T/2);
        clk = 1;
        #(T/2);
    end
    
    initial
    begin
        clr = 0;
        start = 0;
        
        rst_n = 0; #T;
        rst_n = 1; #T;
        
        start = 1; #T;
        start = 0;
    end
endmodule

This is the simulation result of the counter module as FSM.

3. Conclusion

In this tutorial, we covered the sequential circuit and several design examples of sequential circuit.

Pong P. Chu, FPGA Prototyping by Verilog Examples,

Setup And Hold Time,

Vivado: Finding the "maximal frequency" after synthesis,

There are three main timing parameters of a D FF: TsetupT_{setup}Tsetup​ (setup time), TholdT_{hold}Thold​ (hold time), and TcqT_{cq}Tcq​. TsetupT_{setup}Tsetup​ is the time before the active clock edge, when the data signal is not allowed to change its value. TholdT_{hold}Thold​ is the time after the active clock edge, when the data signal is not allowed to change its value. TcqT_{cq}Tcq​ is the time required to propagate the value of d to q at the rising edge of the clock signal.

The d signal must be stable during TsetupT_{setup}Tsetup​ and TholdT_{hold}Thold​ to prevent the FF from entering the metastable state.

The timing of a sequential circuit is characterized by the maximal clock frequency, fmaxf_{max}fmax​. The reciprocal of fmaxf_{max}fmax​ specifies TclockT_{clock}Tclock​, the minimal clock period, which can be interpreted as the interval between two sampling edges of the clock. The minimal clock period can be obtained by adding the propagation delays and setup time constraint:

Tclock=Tcq+Tcomb+TsetupT_{clock}=T_{cq}+T_{comb}+T_{setup}Tclock​=Tcq​+Tcomb​+Tsetup​
fmax=1Tclock=1Tcq+Tcomb+Tsetupf_{max}=\frac{1}{T_{clock}}=\frac{1}{T_{cq}+T_{comb}+T_{setup}}fmax​=Tclock​1​=Tcq​+Tcomb​+Tsetup​1​
fmax=1Tclock_constr−WNSsetup f_{max}=\frac{1}{T_{clock\_constr}-WNS_{setup}}fmax​=Tclock_constr​−WNSsetup​1​
📻
https://onlinelibrary.wiley.com/doi/book/10.1002/9780470374283
https://semiconshorts.com/2022/12/31/setup-and-hold-time/
https://www.01signal.com/vendor-specific/xilinx/vivado-minimal-period/
zybo_tutorial/part_3 at main · weenslab/zybo_tutorialGitHub
Logo
Figure 1. D flip-flop
Figure 2. Synchronous system
Figure 3. Setup and hold time
Figure 4: Launch and Capture D FF
Figure 5. Register
Figure 6. Simulation waveform of the register
Figure 7. Counter
Figure 8. Simulation waveform of the counter module
Figure 9. Vivado block design for counter module
Figure 10. Summary of resource utilization after synthesis
Figure 11. The details of resource utilization after synthesis
Figure 12. Timing constraint of the counter module
Figure 13. Testing the counter module using the ILA interface in Vivado
Figure 14. Timing diagram design
Figure 15. Counter as FSM module
Figure 16. Simulation waveform of the counter module as FSM