Universal Power Format (UPF) was created to allow EDA tools to identify any voltage mismatch problem areas due to multi-voltage domains that came about due to low voltage domains of various IPs and even core voltage. Legacy IPs like PLL may operate from higher voltage domain such as 1.8V but core voltage domain may operate at 1.0V. This mismatched voltage domain could cause semiconductor issues if no voltage translators are inserted. And additionally, if power domains are shutoff, there must be isolation cells inserted to not cause reverse diodes to trigger aka latchup.
Power switch is to shutoff entire voltage domain
Isolation is to not leave outputs of a shutdown voltage domain to float as this would cause the semiconductor chip to latchup.
Level shifter cells are inserted to translate from one voltage domain to another, whether it is down voltage level shifting or up voltage shifting.
Retention cells are to keep certain flip-flops powered to retain certain states and conditions so that upon exiting sleep modes the SoC can be powered up in last known state.
create_power_domain PD_TOP -include_scope: This tells the tool that every single cell, macro, and sub-block from the top level down belongs to this domain (PD_TOP).
set_domain_supply_net: This acts like the main power grid connection, hooking up the primary supplies to every standard cell in the design.
add_port_state & create_pst: While redundant for a chip that never turns off, defining a Power State Table (PST) is a strict requirement for many EDA implementation and formal verification tools to validate that your voltage levels are always consistent.
UPF Example : Power-Gated Domain with Power Switch Cell
## 1. Create Supply Ports (Always-On Global Supplies)
create_supply_port VDD create_supply_port VSS
## 2. Create Supply Nets
create_supply_net VDD ; # Always-On main supply create_supply_net VSS ; # Common ground create_supply_net VDD_switched ; # Switched (gated) power rail for the power domain
VDD_switched Net: This is the virtual power rail. It only connects to the standard cells inside the PD_GATED domain.
create_power_switch: This is the magic command.
-input_supply_port hooks up to the always-on VDD.
-output_supply_port hooks up to the virtual VDD_switched.
-control_port binds the physical switch enable pin to the RTL net (pwr_ctrl_net) driving the shutdown sequence.
-on_state specifies that when pwr_ena is active (typically high/1, depending on your cell library’s design), the switch closes and passes VDD_in through.
PST Update: The Power State Table now validates two legal operating states (state_run and state_gate), allowing implementation tools to properly analyze cross-domain paths and check if isolation cells are required when signals leave the gated domain.
UPF Multi-Voltage, Multi-Domain design
To scale this up to a Multi-Voltage, Multi-Domain design, we need to introduce a second independent voltage supply rail (VDD2).
This configuration establishes two distinct switched power domains (PD_GATED_A and PD_GATED_B). Each domain is powered by a different voltage level and is controlled by its own dedicated power switch cell.
add_pst_state GATE_A -pst top_pst -state {ON_0p90 OFF ON_0p75 ON_0p75 GND}
# Module A running, Module B gated
add_pst_state GATE_B -pst top_pst -state {ON_0p90 ON_0p90 ON_0p75 OFF GND}
# Both blocks gated (Deep Sleep)
add_pst_state ALL_GATE -pst top_pst -state {ON_0p90 OFF ON_0p75 OFF GND}
Critical Verification Checklist for this Setup
Since you are dealing with different voltages alongside power gating, synthesis and verification tools (like SpyGlass CDC/LP or VC LP) will look for two additional architectural structures:
Level Shifters: Required for signals crossing directly between PD_TOP/PD_GATED_A (0.90V) and PD_GATED_B (0.75V) to handle the voltage differential.
Isolation Cells: Required on any output signal leaving PD_GATED_A or PD_GATED_B. When either domain is switched off, its output pins will float, which can cause severe short-circuit current (crowbar current) in downstream always-on logic if not clamped to a constant high or low state.
-isolation_power_net: Must be connected to a supply that stays ON when the domain itself is gated. For PD_GATED_A, we power the isolation cells using the global always-on VDD1.
-location parent: Places the isolation cell outside the gated block (in the parent domain). This ensures the isolation cell itself doesn’t lose power when the domain shuts down.
-isolation_sense low: Means isolation activates when iso_ctrl drops to 0. (Always ensure your RTL Power Management Unit asserts isolation before pulling down the power switch enable).
Level Shifter Details
-rule: Explicitly states the voltage transition direction (high_to_low or low_to_high). Implementation tools read the PST to see that VDD1 is $0.90\text{V}$ and VDD2 is $0.75\text{V}$ to accurately map the physical standard cells.
-location self vs parent: Controls where the level shifter cell is physically placed in the layout hierarchy to guarantee it always has access to both the source and destination power rails required to perform the shift.
UPF Retention Cell Example
To keep the critical internal state (like a configuration register or program counter) intact when your gated domains power down, you need to implement Retention Cells.
Retention flip-flops feature an extra “shadow latch” that is hooked up to an always-on power supply. When the main power rail goes dead, the data is preserved in this backup latch and automatically restored when power returns.
The basic interface to APB (Advanced Peripheral Bus) consists of these two input signals to generate the request : PSELx and PENABLE. These control the APB state machine to transfer the read and write data. To limit the write transfers, transactions can be held off to be single transactions with the output of the peripheral as PREADY. When PREADY =1 , the peripheral can accept transactions and when PREADY=0, then peripheral will NOT accept transactions. It also breaks transfers into a simple 2-cycle process (a setup phase and an access phase). Other APB signals like PWRITE are driven by the converter from AXI interface. Note that once the APB slave peripheral like a memory has accepted the transfer it will drop the PREADY=0 to accept next transfer.
Key Design Details:
AXI4-Lite Support: Optimized for the lightweight protocol, allowing single-beat, non-bursting transactions where the AR or AW channels lock the bus until complete.
APB Protocol Alignment: Uses the two-cycle sequence dictated by APB; cycle 1 (SETUP with PSEL high and PENABLE low), followed by cycle 2 (ACCESS with PSEL high and PENABLE high).
Wait State Handling: The ACCESS state waits dynamically on the peripheral by checking m_apb_pready, stalling the AXI response until the peripheral is finished with current single-beat operation.
APB Finite State Machine
APB Example Waveform
The waveform below illustrates this simple APB protocol for a write and read cycle.
Write Transaction (Cycles 2 to 5):
SETUP Phase (Cycle 2):PSELx goes high, PWRITE is high (Write operation), and the address/data buses are driven. PENABLE stays low.
ACCESS Phase – Wait State (Cycle 3):PENABLE goes high. The slave isn’t ready yet, so PREADY stays low. The FSM holds this state.
ACCESS Phase – Complete (Cycle 4): The slave drives PREADY high, indicating it has successfully sampled PWDATA.
IDLE Phase (Cycle 5): The transfer finishes; PSELx and PENABLE drop back to low.
Read Transaction (Cycles 6 to 8):
SETUP Phase (Cycle 6):PSELx goes high, and PWRITE goes low to signal a read command.
ACCESS Phase (Cycle 7):PENABLE goes high. In this cycle, the slave’s internal counter allows it to respond immediately, driving PREADY high and placing 0xDEADBEEF onto PRDATA.
Return to IDLE (Cycle 8): The master captures PRDATA on the rising edge, and the bus returns to idle.
Systemverilog code for AXI4 Lite to APB Converter
We start with the AXI4 Lite slave interface signals since it is a target which is driven by an AXI4 master. So most signals are input except for the *ready which are outputs back to the AXI master to control data transfers.
The systemverilog uses a typedef to define the APB Finite State Machine states : {IDLE SETUP,ACCESS}
AXI has 5 channels : AW, W (write data), B (wr response), AR, R (read data)
APB has one channel for both write and read with signals :
if (current_state == ACCESS && m_apb_pready && !pwrite_reg) begin
s_axi_rvalid <= 1'b1;
s_axi_rdata <= m_apb_prdata;
s_axi_rresp <= m_apb_pslverr ? 2'b10 : 2'b00;
end else if (s_axi_rready) begin
s_axi_rvalid <= 1'b0;
end
end
end
endmodule
Testbench for AXI4 Lite to APB Converter with SVA (Systemverilog Assertion)
Expected Write Waveform Progression for mock peripheral memory
AXI Entry:s_axi_awvalid & s_axi_wvalid drive high. The bridge asserts s_axi_awready & s_axi_wready, absorbing the payload.
APB Setup Cycle:m_apb_pselx spikes to 1 while m_apb_penable remains at 0. m_apb_paddr and m_apb_pwdata present their target values.
APB Access Cycle: The subsequent cycle forces m_apb_penable to 1. If your randomized peripheral wait cycles kick in, m_apb_pready lingers at 0, forcing the bridge to extend this step safely.
Completion: Once m_apb_pready matches at 1, m_apb_penable and m_apb_pselx safely switch off on the next clock, while s_axi_bvalid shoots high to let the master know the sequence wrapped up safely.
`timescale 1ns/1ps
module tb_axi_lite_to_apb;
// Parameters
localparam ADDR_WIDTH = 32;
localparam DATA_WIDTH = 32;
localparam CLK_PERIOD = 10;
// Clock and Reset
logic clk;
logic rst_n;
// AXI Lite Signals
logic [ADDR_WIDTH-1:0] s_axi_awaddr;
logic [2:0] s_axi_awprot;
logic s_axi_awvalid;
logic s_axi_awready;
logic [DATA_WIDTH-1:0] s_axi_wdata;
logic [(DATA_WIDTH/8)-1:0] s_axi_wstrb;
logic s_axi_wvalid;
logic s_axi_wready;
logic [1:0] s_axi_bresp;
logic s_axi_bvalid;
logic s_axi_bready;
logic [ADDR_WIDTH-1:0] s_axi_araddr;
logic [2:0] s_axi_arprot;
logic s_axi_arvalid;
logic s_axi_arready;
logic [DATA_WIDTH-1:0] s_axi_rdata;
logic [1:0] s_axi_rresp;
logic s_axi_rvalid;
logic s_axi_rready;
// APB Signals
logic [ADDR_WIDTH-1:0] m_apb_paddr;
logic m_apb_pwrite;
logic m_apb_pselx;
logic m_apb_penable;
logic [DATA_WIDTH-1:0] m_apb_pwdata;
logic [DATA_WIDTH-1:0] m_apb_prdata;
logic m_apb_pready;
logic m_apb_pslverr;
// Instantiate Design Under Test (DUT)
axi_lite_to_apb #(
.AXI_ADDR_WIDTH(ADDR_WIDTH),
.AXI_DATA_WIDTH(DATA_WIDTH),
.APB_ADDR_WIDTH(ADDR_WIDTH),
.APB_DATA_WIDTH(DATA_WIDTH)
) dut (.*);
// Clock Generation
always #(CLK_PERIOD/2) clk = ~clk;
// Simulated APB Peripheral Model (Dynamic Response)
-input (or -i): Specifies your Tcl/RDC configuration script containing your parameters. -project: Saves the generated structural and functional database into a target directory. This allows you to open Real Intent Debug Gui directly later using idebug -project meridian_project to visualize faults and cross-reference violating logic to your RTL source code
-input: Feeds the structural analysis algorithms with the clock relationships, exclusions, and parameters defined above. -project: Consolidates design parameters and CDC errors into a unified project file database. You can directly inspect structural failures, meta-stability reports, and visual schematics by calling idebug -project cdc_verification_proj.
The complete Tcl architecture executes structural verification, detecting metastability threats, missing synchronizers, data reconvergence issues, and clock-gating structural faults across the entire design layout.
The JTAG bus has been around for many years and consists of a simple set of pins :
Input TDI, TCK, TMS, TRST
Output TDO.
Based upon the serial input bits on TDI, the JTAG FSM is stepped through the various states depending upon state of TMS = 1 or 0.
See diagram below showing JTAG physical interface, JTAG FSM and JTAG waveform example.
Controlling the FSM
To make the FSM move, you push a sequence of 0s and 1s to the TMS pin. For example:
Resetting: Holding the TMS pin high (1) for 5 consecutive clock cycles will always force the FSM back to the Test-Logic Reset state.
Shifting Data: To shift data in, you walk the FSM through the Capture, Shift, and Update states using a precise pattern of TMS toggles.
The JTAG state machine’s design is brilliant because its active shifting happens on the rising edge of the clock, while the final latching happens on the falling edge. This half-cycle delay prevents wild “thrashing” of outputs, which keeps your hardware safe while bits are flying by.
The state machine is simple, comprising two paths:
The data register (DR) path (shown in green), used for loading instructions
The instruction register (IR) path (shown in blue), used for reading/writing data from/to data registers, including the boundary scan register (BSR)
Assuming the state machine begins at Test-Logic-Reset, we begin by clocking a TMS = 0 to enter the Run-Test/Idle state, then clock a TMS = 1 to begin selecting a path.
The Core Loop: Symmetrical Paths
The FSM contains two main, symmetrical branches: one for the Instruction Register (IR) and one for the Data Register (DR). Each branch follows the exact same three steps to shift data into the chip:
Capture: The chip takes a snapshot of its current state or data and loads it into a parallel shift register. Shift: The chip slowly passes this data out of the TDO (Test Data Out) pin while shifting new test data in from the TDI (Test Data In) pin, bit by bit. Update: The chip latches the new shifted data onto the hardware, completing the command.
Key States Explained
Test Logic-Reset: The default starting state. All JTAG operations stop, and the chip returns to its normal, everyday function. Run-Test/Idle: A holding state. The chip sits idle or runs a specific built-in test while waiting for the next command. Need TMS=0 to transition from Teset-Logic-Reset to Run-Test/Idle state. Select-DR-Scan & Select-IR-Scan: The decision points. These states decide if you want to route test data (DR) or load a new command/instruction (IR). Default is Select-DR-Scan but an additional TMS=1 state with TCK will step to Select-IR-Scan state. Capture-IR / Shift-IR / Update-IR: The states used to load a new “instruction” (like “read device ID” or “activate boundary scan”) into the chip. Capture-DR / Shift-DR / Update-DR: The states used to send or receive the actual payload data. This will pass the JTAG shifted data register to your local register that will use it.
TAP Controller FSM with more detailed description
TAP Controller Architecture
The TAP controller manages the state machine, and depending on the state selected, the output MUX is switched.
The two paths are:
The instruction capture-shift path
The data capture-shift path
Note how the boundary-scan register, which comprises the boundary-scan cells around the IO pins, is one of the data registers. Data registers are shift-registers, and can be of arbitrary length.
Capture, Update, and Shift States
The most ‘active’ states are the capture, shift, and update states.
The capture state is perhaps the most mysterious, performing different actions for the data path compared to the instruction path. Here, capture means parallel loading data into a shift register, as opposed to shifting the data in serial into the register. Shift means, as one might expect, shifting data into the shift register. Then, the update stage latches the register, and the state machine can reset.
Specifically, Capture-DR is the state where, if needed, test data can be parallel-loaded into the shift-capture path of the current data register. (The current data register is set by the current instruction which was previously set.) This means that data is loaded, in parallel, into the data register selected by the current instruction, as opposed to shifting in.
Capture-IR is used for fault isolation in the JTAG system, though the standard is vague about its purpose. A fixed logic value (which must end in {…01}) is loaded in parallel into the instruction register shift-capture path. This is to say, the instruction register is parallel-loaded (instead of shifting) with a fixed logic value.
The Shift-DR and Shift-IR states are the main states for serial-loading data into either data registers or the instruction register. While the state machine is in one of these states, TMS is held LOW, until the shifting operation is complete. The Update-DR and Update-IR states latch the data into the registers, setting the data in the instruction register as the current instruction (and in doing so, setting the current data register for the next cycle).
IEEE 1500
The IEEE 1500 standard defines a scalable, reusable hardware wrapper and interface used to test embedded intellectual property (IP) cores within complex System-on-Chips (SoCs). It facilitates “plug-and-play” testing, allowing designers to safely isolate and test individual circuit blocks without exposing proprietary core details.
IEEE 1500 defines a standard for test access of cores within a big chip. For small designs, we just do connections at top level, and pass on signals from one module to other. But for big designs, it can get very complex. For such designs, we treat each block as a chip in itself, having it’s own controller for dft purpose, which handles all internal details of dft. Then, at top level we justhave a top level 1500 controller, which connects and controls these block level 1500 controllers. All connections are controlled via JTAG pins. These pins go into top level TAP controller, which processes and passes all the appr signals to each block.
We define standard port interface for IEEE 1500, to connect different blocks. Most signals are prefixed with W which stands for wrapper. WIR = wrapper instruction reg, while DR = data reg (WDR or wrapper data reg name not used for whatever reason). These IO signals are:
WRCK/WRCLK = Clock. This is actually connected to chip JTAG pin TCK.
WRSTN = Reset (active low). This resets all reg in WIR to 0, indicating func mod. This is actually connected to chip JTAG pin TRSTN.
SHIFTWR/SHIFTDR = Shift WIR or DR. With this signal high, Shift reg gets connected b/w WSI and WSO and starts getting values shifted in/out
UPDATEWR/UPDATEDR = Update WIR or DR. With this signal high, Update reg gets updated with values in shift reg. These signals coming out of Update reg thenget stored in bunch of internal reg, which control all test related stuff in Func logic (i.e bist control signals, bypass, etc)
SELECTWIR = Operate on WIR. (we can have multiple SELECTWIR1, SELECTWIR2, etc if we have multiple partions within WIR, which we want to activate separately)
WSI / WSO Scan In/Out Data. These are single bit line for scanning data in and out of IR/DR. These are connected to chip JTAG pin TDI/TDO via daisy chain. First block’s WSI comes from top TAP controller, which in ultimately connected to TDI pin, WSO pin connects to next block WSI pin, and so on. Last block’s WSO pin goes to top TAP controller, which finally connects to chip TDO pin. The i/p pin WSI is captured on +ve clk edge, while o/p pin WSO is fired on -ve clk edge. This follows the same convention as for all other pins of JTAG which are fired on -ve edge, but captured on +ve edge of clk.
WPI /WPO Parallel In/Out Data. These are multi bit bus, and used for scanning data in and out of functional flops (i.e scan chain stitching of functional flops). These are same as SDI (scan Data in) and SDO (scan data out) pins that are used in designs for scan data in/out. The reason, we have a bus is to have mutiple chains of scan in/out for big designs (since they may have millions of flops, and shifting data in/out via just one pin is going to take hours). WPI/WPO are captured/fired on same clk edge as WSI/WSO.
Interface Ports and Signals
The IEEE 1500 interface standardizes access points to feed test patterns into the core. It defines two main port types, which can be used individually or combined:
Wrapper Serial Port (WSP): Mandatory pins that allow shifting data into and out of the core sequentially. Includes:
WSI (Wrapper Serial Input)
WSO (Wrapper Serial Output)
WRCK / WRCLK (Wrapper Clock, typically linked to the system JTAG TCK)
WRSTN (Wrapper Reset, active low)
SHIFTWR (Shift Control)
Wrapper Parallel Port (WPP): Optional, scalable ports that allow massive parallel data transmission to meet high-bandwidth test requirements.
Core Interface Architecture
The standard separates internal core testing from chip-level operations by enveloping the core in a specific wrapper interface.
The IEEE 1500 interface consists of two primary hardware components:
Wrapper Boundary Register (WBR): Composed of wrapper cells on each functional input and output pin. It acts as an isolation barrier, allowing the application of test vectors and serialization/deserialization of data between the core under test (CUT) and surrounding logic.
Wrapper Instruction Register (WIR): Configures the wrapper into various test modes and initiates all test activities (e.g., INTEST for internal core testing, EXTEST for external interconnect testing).
Wrapper Bypass Register (WBY): Provides a short path through a wrapped core so test data can reach other modules without unnecessary delays.
IEEE 1500 Core Wrapper Architecture (Top Left): This section shows how an embedded core is isolated. Key components like the Wrapper Instruction Register (WIR), Wrapper Data Registers (WDR), and the vital Wrapper Bypass Register (WBY) and Wrapper Boundary Register (WBR) are highlighted. These registers are essential for shifting test data into the core while bypassing others.
IEEE 1500 Standard Interface Signals (Top Right): This provides a breakdown of the required signals, including those for serial data transfer (WSI/WSO) and parallel data transfer (WPI/WPO). The table defines the standard abbreviations used in the diagram (e.g., WRCK for Wrapper Clock).
Sample Protocol Waveform: INTEST and EXTEST (Bottom): This timing diagram shows a sequential test.
First, the INTEST (Internal Test) instruction is loaded, configuring the wrapper boundary to test the core logic internally.
Next, the EXTEST (External Test) instruction is loaded, which is often used for interconnect testing between cores.
The diagram illustrates the corresponding states of the Wrapper Clock, the Wrapper Shift signal, and the Wrapper Data signals, showing how the Wrapper Boundary Register is loaded with test patterns and then captured and shifted out.
IEEE P1687
IEEE 1687, commonly known as IJTAG (Internal JTAG), is an IEEE standard that provides a uniform, automated way to access and control embedded instruments within a semiconductor device. It creates a bridge between standard external chip pins and internal testing features.
Why IJTAG is Needed
Modern chips are incredibly complex and contain thousands of tiny monitoring tools (called “instruments”) used for testing, debugging, and post-silicon validation. Traditional methods required custom wiring for every tool, which was highly inefficient. IJTAG standardizes the architecture so these instruments can be easily accessed without changing the core design of the chip.
The Two Core Languages
IJTAG achieves its flexibility by separating the hardware design from the software commands. It uses two specialized languages:
ICL (Instrument Connectivity Language): Describes the hardware structure of the on-chip network. It details how data flows through the chip and which pathways lead to which instruments. PDL (Procedure Description Language): Describes the software instructions used to test or operate the instruments. PDL tells the system what data to send and what results to expect, without worrying about the physical path.
The Translation (Retargeting) Process
One of the biggest advantages of IJTAG is the retargeting engine. A test engineer can write a PDL script for a single, specific instrument inside the chip. The IJTAG software automatically takes that simple script and translates it into chip-level scan vectors. This means the software calculates exactly what needs to be fed into the main pins of the chip to reach the target instrument.
Key Hardware Components
Gateway: The entry point where test data enters the on-chip IJTAG network.
SIB (Segment Insertion Bit): A SIB acts like a digital switch. It allows the testing system to dynamically turn on or bypass specific branches of the instrument network. This shortens the data path, saving time and memory during testing.
Design for Test (DFT) insertion alters your chip’s circuitry by adding test modes, scan chains, and clock multiplexers. Without the proper DFT constraints, Static Timing Analysis (STA) and Clock Domain Crossing (CDC) tools will generate thousands of fake errors. You must constrain these test signals properly to isolate functional clock crossings.
1. Fix the Scan Enable (SE) Signal
Your Scan Enable (SE) signal tells the chip whether it is operating normally or in test mode. During functional analysis, you must tell the tool that SE is completely inactive so it does not evaluate impossible test paths. [1]
Apply this Synopsys Design Constraint (SDC) or Xilinx Design Constraint (XDC) early in your flow: set_case_analysis 0 [get_ports scan_enable]
2. Isolate Test Clocks
DFT often forces fast clocks to interact during testing. If these paths are reported by your CDC tool as un-synchronized, your tool will flag endless violations. You must treat these as asynchronous clock domains.
Instruct the tool to ignore timing on test crossings by specifying the domain: set_clock_groups -asynchronous -group [get_clocks test_clock] -group [get_clocks functional_clock]
3. Constrain Datapaths
For valid CDC paths that exist to test asynchronous logic, you must limit the delay so that tools can successfully sample the data. Constrain the maximum data path delay to avoid violating setup and hold times.
Use the data path constraint command: set_max_delay -datapath_only -from [get_clocks source_clock] -to [get_clocks dest_clock] <delay_value>
4. Separate DFT Domains
A best practice during DFT scan insertion is to group scan cells from different clock domains into separate scan chains. This prevents metastability from destroying your test data. If you use tools like Tessent Scan, you can add this option to ensure all scan chains belong to the same clock domain:
add_scan_mode -single_clock_domain_chains
Why This Matters
If you do not set these constraints, testing logic will look like illegal RTL clock domain crossings. By defining case analysis and grouping your clock domains properly, you allow your EDA tools to focus only on real functional bugs.
CDC Constraints
About CDC Constraints
Clock Domain Crossing (CDC) constraints apply to timing paths that have a different launch and capture clock. There are synchronous CDC and asynchronous CDC depending on the launch and capture clocks relationship and on the timing exceptions set on the CDC paths. For example, CDC paths between synchronous clocks but covered by false path constraints are not timed, and consequently are treated as asynchronous CDCs.
Asynchronous CDC paths can be safe or unsafe. The terminology of safe and unsafe for asynchronous CDC paths is different from the terminology used for inter-clock timing analysis (see report_clock_interaction). An asynchronous CDC path is considered safe when it uses a synchronization circuitry to prevent metastability of the capture sequential cell.
The timing analysis of CDC paths can be fully ignored by using
set_false_path or set_clock_groups constraints,
or partially analyzed by using
set_max_delay -datapath_only.
In addition, the multibit CDC paths capture time spread can be constrained using the
set_bus_skew constraint.
Constraining Bus Skew
About Bus Skew Constraints
The bus skew constraint is used to set a maximum skew requirement between several asynchronous CDC paths. The bus skew is not the traditional clock skew associated with a timing path. Instead, it corresponds to the largest capture time difference across all the paths that are covered by a same set_bus_skew constraint. The bus skew requirement applies to both Fast and Slow corners, but it is not analyzed across the corners.
The intent of the bus skew constraint is to limit the number of source clock edges that can launch a data and be captured by a single destination clock edge. The tolerance depends on the CDC synchronization scheme used for the constrained paths. The bus skew constraint is typically used for the following CDC topologies:
Gray-coded bus transfer, such as in asynchronous FIFOs
Multi-bit CDC implemented with CE, MUX, or MUX Hold circuitry
Configuration registers
Although the set_bus_skew command does not prevent a bus skew constraint to be set on a safely timed synchronous CDC, such a constraint is not needed. The setup and hold checks already ensure a safe transfer between two safely timed synchronous CDC paths.
The CDC scenarios for bus skew constraints are:
Asynchronous CDC covered with set_clock_groups
Asynchronous CDC entirely covered with set_false_path and/or set_max_delay -datapath_only
Synchronous CDC paths covered with set_false_path and/or set_max_delay -datapath_only
The bus skew constraint is not a timing exception; rather, it is a timing assertion. Therefore, it does not interfere with the timing exceptions (set_clock_group, set_false_path, set_max_delay, set_max_delay -datapath_only, and set_multicycle_path) and their precedence.
Why CDCs Break During DFT
Shift vs. Capture: In normal operation, circuits use synchronizers (like FIFOs or multi-flop chains) to safely pass data between clocks. During DFT, the scan path bypasses this logic, linking flip-flops in a raw chain. This exposes paths that are unsafe for test data.
Clock Skew: Scan data shifts using different clock phases. This can cause signals to arrive too late at the receiving flip-flop, resulting in data loss.
How to Fix Scan CDC Issues
Lock-Up Latches: DFT tools insert “lock-up latches” between scan flip-flops that sit on different clock domains. These latches act as a temporary buffer. They hold the data stable until the next clock cycle begins, preventing data loss.
Diagram Explanation
Clock Domains: The diagram is split vertically into two distinct domains: a faster Clock Domain A (Source) and a slightly slower Clock Domain B (Destination).
Potential Problem: During a scan-shift operation, data must travel from the final scan flip-flop (CFF1) in Domain A to the first scan flip-flop (CFF2) in Domain B. If CFF2 captures the data too quickly after it is launched by CFF1, a hold-time violation can occur, resulting in invalid test data.
The Solution (Lockup Latch): By placing a lockup latch between the domains and triggering it on the opposite clock edge of the preceding flip-flop, we insert a mandatory half-clock cycle of delay (Hold Padding). This added delay ensures the data remains stable before CFF2 samples it, guaranteeing reliable cross-domain communication during testing.
Test Clocks: Testers often group or slow down clocks globally during test modes. This allows the chip to capture data at a relaxed speed without timing mismatches.
Verification and Sign-Off
Because DFT insertion alters the netlist, standard RTL (Register-Transfer Level) CDC checks are not enough. Engineers perform dedicated CDC sign-offs on the DFT netlist.
Identify Violations: Use tools like Real Intent or VLSI Guru to spot where un-synchronized scan paths cross clock domains.
Isolate Domains: Use DFT-specific clock controllers to mask paths or bypass asynchronous crossings so that Automatic Test Pattern Generation (ATPG) works properly.
To check for Clock Domain Crossing (CDC) issues using Synopsys SpyGlass, you will primarily use the SpyGlass CDC product (built on the underlying GuideWare methodology). The process involves setting up your design, specifying constraints (clocks, resets, and domain definitions), and running the CDC goals.
SPYGLASS FOR CDC CHECKS
Here is a comprehensive breakdown of the essential SpyGlass commands and a standard Tcl script structure used to run a CDC analysis.
1. Core SpyGlass CDC Commands
These are the primary Tcl commands used inside a SpyGlass project file (.prj) or interactive shell.
Project Setup Commands
new_project <project_name> -over – Creates a new SpyGlass project directory.
set_option top <top_module_name> – Specifies the top-level design module.
read_file -type sgdc <file_name>.sgdc – Critical for CDC. Reads the SpyGlass Design Constraints file where clocks, resets, and abstract rules are defined.
Goal Management Commands
current_goal <goal_name> -top <top_module> – Selects the specific CDC analysis rule-set to execute.
run_current_goal – Executes the selected goal on the design.
2. Standard CDC Goals Sequence
SpyGlass CDC checks are performed in a sequential, iterative pipeline. You start with basic setup checks and move toward complex structural and functional verification.
Goal Name
Purpose
What it Checks
cdc/cdc_setup
Setup Validation
Validates your SGDC file definitions. Checks if all clocks/resets are defined and correctly propagated.
cdc/cdc_setup_check
Enhanced Setup
Identifies unconstrained clocks, black-boxes, or incorrectly defined constants.
cdc/clock_reset_integrity
Clock/Reset Quality
Checks for glitches, bad muxing, or deep combinational logic in clock/reset paths.
cdc/cdc_verify_struct
Structural Verification
The core CDC check. Finds missing synchronizers, data holding violations, and structural crossings.
cdc/cdc_verify
Functional Verification
Runs formal/functional checks on handshake protocols and FIFO depth/stability.
Template SpyGlass CDC Run Script (run_cdc.tcl)
You can aggregate these commands into a single Tcl script to automate your analysis:
# Step 3: Run comprehensive functional CDC checks (Optional/Advanced)
current_goal cdc/cdc_verify -top my_top_module
run_current_goal
# ----------------------------------------
# 5. Save and Close
# ----------------------------------------
save_project
4. The SGDC File (Why Commands Aren’t Enough)
SpyGlass cannot accurately identify CDC violations without a SpyGlass Design Constraints (.sgdc) file. It tells the tool which signals belong to which clock domains.
Below are the critical commands used inside the .sgdc file:
Tcl
# Define Clocks and assign them to distinct domains
After executing your script, you can view the results via command-line reports or the GUI:
Via GUI (Recommended for CDC debugging):Bashspyglass -project my_cdc_project.prj & Once the GUI opens, navigate to the Incremental Schematic window to trace the exact path of a unsynchronized violation (e.g., source flip-flop $\rightarrow$ combinational logic $\rightarrow$ destination flip-flop).
Via Text Reports: Check the my_cdc_project/my_top_module/<goal_name>/spyglass_reports/ directory. Look specifically for moresimple.rpt for a clean summary of the warnings and errors.
We live in an asynchronous world where events happen all the time without us knowing it. However, we also live in a clocked world where we need to know the absolute time so that we can properly handle these asynchronous events. The role of CDC (clock domain crossing) / RDC (reset domain crossing) is to handle these asynchronous events externally to a semiconductor chip or SOC as well as internally where there will be multiple clock domains with signals crossing back and forth between these clocked interfaces. Some CDC/RDC principles are gleamed from my years of work experience and some are learned from Ganga Nunna on LinkedIn. I have categorized into my own general topics based upon my experience.
What is CDC / RDC ?
What is CDC ?
CDC is the process of transferring signals and/or data between two logic circuits operating in different clock domains. (e.g. one could be 1 MHz frequency and another could be 1000 MHz). It enables safe communication between blocks that do not share the same clock, frequency, or phase. So handling signal and/or data crossings from slow to fast domains need to handled safely to ensure proper operation of the logic circuits.
Why do we need CDC ?
Modern SoCs like AI Compute chips or Servers or Space target chips run on multiple clocks for performance, power, architecture, and functional reasons. One always desires maximum performance with minimal power and minimal cost. When a signal crosses domains without proper signal handling, this may cause various issues in the SoC such as : • Metastability – signals fluctuate between logical high and logical low over a period of time. This can lead to data corruption. • Data corruption – instead of an expected signal being logically high its actually logically low • Data loss / incoherence – because certain signals are logically low instead of high data transfers do not occur and so data loss. • Functional failures – on a larger picture once various signals incorrectly stay logically high or low, functional failures can occur. Therefore, CDC techniques are essential to ensure reliable SoC chip operation.
When does CDC occur ?
CDC occurs whenever: • Two modules operate on different clocks such as 1 MHz versus 1000 MHz • Clock frequencies differ (e.g. 1 MHz versus 1000 MHz) • Clocks come from separate PLLs / oscillators either internally or externally to the SoC. • The phase relationship between clocks is unknown or asynchronous. (This is important as one can have the same clock frequency but operate in a different phase).
Where is CDC applied in the SoC ?
CDC is utilized in many SoC designs due to the large usage of multi-clock domains of multiple IPs (e.g. PLL, HBM,CPU, Ethernet, USB Serializers like a keyboard): • Multi-CPU cores and multi-clock IPs (PLL, HBM, DRAM, Ethernet, USB) • APB <-> AXI <-> AHB <-> CHI <-> Customized bridges • FIFO-based data movers. (FIFOs are needed when write bursts are faster than the read bursts and need to use FIFO as temp local data storage) • Slow–fast domain handshakes such as interrupts • Interrupts and resets (important signals that need to be properly handled) • DMA, sensor interfaces, network-on-chip paths between 2 different clock domains. Anywhere 2 or more clocks differ in frequency or phase → CDC is required.
What is metastability and why I should care about it ?
What is metastability ?
Metastability is when a signal coming out of a flip-flop is in indeterminate state : neither a logical one nor a logical zero. This block digram shows what occurs from the first flip-flop which feeds into a second flop which form a 2-flip-flop synchronizer pair. Metastability occurs when the setup or hold required time for the first flip-flop is violated. The second flip-flop is needed to resolve the first flip-flop issue.
A flip-flop needs stable data (logic levels for high and low) around the clock edge to satisfy its timing requirement for : • Setup time • Hold time
If data changes too close to that edge, the flip-flop cannot decide between 0 or 1 → it enters an unstable and unknown analog state as seen below:
This unstable temporary condition is called metastability.
What’s the internal semiconductor device behavior ?
Internally a flip-flop has: • Two cross-coupled inverters • Forming a positive feedback loop
If input changes during sampling: • Both inverters see conflicting voltages • Internal nodes fight • Output becomes unstable → metastable
It’s like balancing a pencil on its tip — not stable.
What’s the output behavior look like ?
• Output rises or falls very slowly • Stays in-between 0 and 1 • Suddenly settles to a random final value
Why should I care about metastability ?
One needs to pay close attention to metastability because having indeterminate logical signals : 1 or 0, can cause the logic circuit to behave erratically eventually causing functional failure which will cause SoC or system failure. In the digital domain point of view all signals must be either a logic 1 or a logic 0 and properly controlled.
It can cause: • Random output value • Extra propagation delay • Glitches • Wrong control decisions • Corrupted state machines • Multi-bit mismatch • Lost or duplicated events
These failures can break entire chip subsystems and eventually the entire system.
What is MTBF (Mean Time Between Failure)
Metastability cannot be completely eliminated or removed, but its probability can be reduced.
MTBF = Expected average time between metastability-induced failures.
In general, to increase MTBF: • Use 2-FF or 3-FF synchronizers • Give more time for settling • Reduce the toggle rate of async signals • Use robust flip-flops (some fabs create standard cells libraries with hardened or specially designed flip-flops just for reduced MTBF)
Good CDC design can push MTBF to millions of years — meaning practically zero failure in product lifetime.
What logic / RTL design principles to apply for CDC :
We reduce probability of MTBF occuring by using these techniques: • 2-FF synchronizer • 3-FF synchronizer • Handshake protocols • Async FIFOs • Gray coding • CDC constraints
Ultimate Goal is to give the metastable output enough time to settle before use which can mean slower clock but this defeats the purpose of operating at higher frequencies. So best logical effort is to apply the techniques above.
What type of failures occur in the digital design when CDC / RDC is violated ?
It can cause at a low level: • Random output value • Extra propagation delay • Glitches • Wrong control decisions • Corrupted state machines • Multi-bit mismatch • Lost or duplicated events
At higher level these issue may appear:
• Random modes of system operation • Frozen operation and will need hard reboot (push the power off and on) • Wrong and duplicate data transfers
Why use CDC/RDC tool and not STA to help catch CDC / RDC issues ?
Role of STA in SoC design
Static timing analysis (STA) is mainly used to time set up and time hold times in a logic circuit and not catch cross domain crossing issues.
STA works perfectly only when launch and capture flip-flops use the SAME clock domain, meaning one fully knows:
• Clock arrival time • Clock delays • Clock phase difference • Data arrival time and data path delay
STA assumes a known, predictable relationship between the two clocks
But modern SoCs contain:
• Multiple PLLs from general clocks to high-speed serializers (e.g. UCIe, USB, PCIE, Ethernet, AXI, AXI, APB) • Different clock frequencies (e.g. 1 MHz versus 1000 MHz) • Unknown clock phases • Clock dividers / clock muxes (one may need to use clock divider to slow a clock so complex logic like multipliers can be performed in the clock period) • Multiple asynchronous subsystems (e.g. UCIe, USB, PCIE, Ethernet, AXI, AXI, APB)
• Known frequency + known phase → ✔ STA works • Known frequency + unknown phase → ✖ STA fails (✔ use CDC) • Unknown frequency and phase → ✖ STA cannot be used (✔use CDC)
Whenever the phase is unknown, STA cannot determine setup/hold timing. These paths will be treated as “unconstrained” and cannot be measured using STA. So it cannot guarantee that data will not change near the sampling edge.
What checks are done by STA ?
STA detects only timing-based failures such as:
• Setup time → data must arrive early enough • Hold time → data must remain stable long enough • Delay, skew, jitter → path timing must meet constraints
• clock latency → clock buffers are formed serially so adding up these buffers becomes the clock latency
• Setup Timing Analysis → data arrives before the required window • Hold Timing Analysis → data must not change near the clock edge
STA assumes the timing can be controlled and predicted — But this is NOT true when clocks are asynchronous or unrelated.
The paths that STA cannot control or predict are treated as “unconstrained” and these must be carefully analyzed and constrained. Usually paths to the first flip-flop stage are treated as false paths with “set_false_path -to DFF1/D” so that any long paths are not timing analyzed as they may trigger a false positive setup timing violation. So use CDC to analyze this path.
So STA cannot protect us from CDC failures.
Why use CDC analysis instead of STA analysis ?
When two flip-flops run on different clocks, the data changes at:
• Unknown times • Unpredictable phases • Random boundaries
This virtually guarantees repeated setup and hold violations at the domain boundary. This will cause metastability which is illustrated in the previous metastability diagram.
STA cannot calculate timing when timing itself is unpredictable.
So as the acronym implies : CDC , it is a cross clock (cross phase) domain crossing check at interfaces within a SoC.
What are the failures seen for CDC ?
Data Corruption
This occurs when the destination flip-flop samples metastable data.
Can result in: • Wrong logic values • Unpredictable behavior • FSM jumping to invalid states • Interrupt mis-triggering
Root cause: Async data toggles near the capture clock edge which leads to setup/hold violation. This can be seen as incorrect logic values.
Impact: Control logic becomes unstable and not controllable. Some FSM transitions can occur without the correct input conditions.
Data Incoherence
Multi-bit buses cross domains without proper synchronization.
Example: Source changes 0101 → 1010 Receiver may capture: • 0001 • 0110 • 1011 • Or any random mix from 0000 up to 1111 depending upon the physical semiconductor behavior
Root cause: Each bit meets timing differently and may be captured in different cycles.
Impact: This results in incorrect addresses, data transfer, counter values, and status signals.
Data Loss
If a pulse/event is shorter than one destination clock period, the receiving domain may completely miss it. Basically, the signal is never captured by destination or receiver clock.
Example: Source = 1000 MHz Destination = 1 MHz
The 1 nanosecond pulse is too short to be captured by the slower 1000 nansecond clock period. A 1-cycle source pulse will disappear.
Root cause: Destination clock is too slow to sample the short lived and fast event.
A lesser-known but real hardware risk. When a node becomes metastable, both PMOS & NMOS inside an inverter can partially turn ON at the same time as seen in diagram below :
causing:
Direct current from VDD → GND ➡ Heat generation ➡ Hotspots ➡ Long-term reliability issues (high current and heat always causes breakdown of semiconductor material)
Impact: Block-level and/or even chip-level damage and/or system damage if not handled correctly.
Mapping these SoC problems to CDC fixes
Data Corruption – 2-FF / 3-FF Synchronizers Data Incoherence – Gray Code, Multi-bit Sync Data Loss – Toggle Sync, Pulse Stretching, Handshake Data Duplication – Handshake, Toggle Sync Chip Burning – Correct Synchronization (hardened or robust synchronizers to handle MTBF better)
CDC failures include data corruption, incoherence, loss, duplication, and even hardware stress. Every CDC technique (2-FF sync, toggle, handshake, FIFO) exists to eliminate one or more of these issues.
How to fix CDC ?
We reduce probability of MTBF occuring by using these techniques: • 2-FF synchronizer • 3-FF synchronizer • Handshake protocols • Async FIFOs • Gray coding • CDC constraints
2 stage FF synchronizer (fix data corruption)
What Problems It Solves & Why It Works The 2-FF Synchronizer seen below is the most widely used CDC structure in every SoC. It solves one critical problem extremely well: Data Corruption caused by metastability.
What Problem Does 2-FF Sync Solve?
2-FF synchronizer solves ONLY “Data Corruption”
When Do We Use a 2-FF Synchronizer?
Use it for single-bit control signals, such as: • enable • valid • ready • interrupt • status flags • mode change • reset release (carefully use and keep 2FF together to ensure there’s no setup/hold violation to second FF) ❌ NOT for multi-bit data ❌ NOT for pulses ❌ NOT for counters ❌ NOT for buses
How 2-FF Synchronizer Works
The FF_src outputs a signal in source clock domain which crosses the CDC boundary to FF_sync1 which is destination clock domain. Note that source clock domain and destination clock domain operate independently and is asynchronous to each other. They could be coming from different PLLs on the SoC chip or from offchip PLL.
Async Input from FF_src | [FF_sync1] ← may go metastable | [FF_sync2] ← stable output (SIG_sync) | Synchronized Signal FF_sync1: • Samples async input • May violate setup/hold • May go metastable FF_sync2: • Samples FF_sync1 after one full clock period • Gets enough time for FF_sync1 to settle • Produces a clean and stable output : SIG_sync ====>. Metastability stays inside FF_sync1 and never reaches your logic after SIG_sync.
Why does more time improve MTBF (Mean Time Between Failures) ?
Metastability reliability is measured using MTBF (Mean Time Between Failure). Conceptually: MTBF ∝ e^(Ts / τ) Where: • Ts = settling time • τ = technology constant Adding FF2 increases “Ts” by one full clock cycle → failure probability drops exponentially. That’s why: 1 FF → unsafe 2 FF → safe for almost all control signals 3 FF → used for automotive, aerospace, medical (tradeoff is slightly more latency for increased reliability)
What does 2FF synchronizer Solve vs. What does it not solve ?
Does Solve:
• Data Corruption → output becomes stable
Does NOT solve: • Data Incoherence (multi-bit) • Data Loss (short pulses) • Data Duplication • Chip Burning (FF_sync1 still metastable inside)
✔ No combinational logic between FF_sync1 and FF_sync2 ✔ Both FFs (FF_sync1, FF_sync2) must use destination clock ✔ Use hardened “synchronizer flops” from fab library if available ✔ Place FF_sync1 & FF_sync2 physically close on the die (to avoid setup/hold (clk skew) issues between the flops) ✔ Add a 3rd FF for high-safety designs (will add one clock latency but increases MTBF)
Pulse Synchronizers — How to Prevent Data Loss in CDC
Previous section we saw that 2-FF synchronizers fix data corruption for level signals (signals that generally stay either high or low most of the time). But for pulse signals, 2-FF is NOT enough. We need a pulse synchronizer. The diagram below shows the 2FF synchronizer on the left and the one shot pulse generator on the right which uses back to back flip-flops with XOR gate to generate a one shot pulse.
What Is a Pulse in CDC?
A pulse is a signal that: • Goes HIGH for 1 cycle or more cycles • Then returns LOW
Examples: • Interrupt • Event completion • FIFO full • Timer expiration • DMA request (immediate req) • Single-cycle enables ===>. These signals represent events, not levels — and events must never be missed.
Why Can’t 2-FF Synchronizer Transfer Pulses Safely?
Because pulses can get: Lost – if the pulse is too short and not captured by destination clock Duplicated – if FF_sync1 metastability causes re-sampling Stretched/Shrunk – depending on clock edges (pulse can get reduced depending upon clock edge sampling) So we need something better than 2-FF.
Pulse Loss Example
Source clock: 1000 MHz Destination clock: 100 MHz A 1-cycle pulse in 1000 MHz = 1 ns Destination samples every 10 ns ===> The pulse may occur entirely between two destination edges which means the pulse is lost.
Pulse Duplication Example
If FF_sync1 becomes metastable: • FF_sync2 may sample the transition twice ===> One pulse becomes two pulses This is a general CDC failure.
The Solution: Pulse Synchronizers
There are three main implementation styles:
🟧 Style 1: Toggle-Based Pulse Synchronizer (as seen above) (MOST RELIABLE) Step 1 → Convert pulse to toggle Step 2 → Synchronize toggle using 2-FF Step 3 → Convert toggle change back to pulse ✔ No loss ✔ No duplication ✔ Works for all fast-to-slow CDC crossings ✔ Most widely used in industry
🟧 Style 2: Pulse Stretching Increase pulse width using OR gate and extra flip-flops (e.g., 1 cycle → 3–4 cycles). Useful only when destination clock is known to be faster.
Note : Not general-purpose and very specific usage.
🟧 Style 3: Handshake Pulse Synchronizer Source waits for ack before sending next pulse. ✔ No loss ✔ No duplication ✔ Guaranteed delivery ✔ Best for high-reliability SoCs
Which Method Should You Use?
✔ Toggle Synchronizer → Best general solution ✔ Handshake → Best safety & reliability ✔ Pulse Stretching → Clock-dependent ✘ 2-FF → Never for pulses
Which CDC Concerns Does Pulse Synchronizer Solve?
• Data Corruption → ✔ Yes • Data Loss → ✔ Yes • Data Duplication → ✔ Yes • Data Incoherence → ✘ Not related • Chip Burning → ✔ Avoids metastable retrigger
Pulse Synchronization Summary
Pulse synchronizers prevent data loss and duplication when transferring short events across clock domains. Toggle-based and handshake pulse synchronizers are the most reliable and widely used solutions in industry.
Toggle Synchronizer – Safest Way to Transfer Events Across Clocks
The toggle synchronizer is one of the most reliable CDC techniques for transferring pulses/events across asynchronous clock domains. It creates a toggle state in source clock domain and utilizes the pulse synchronization on destination clock domain. See block diagram below generated by Gemini AI tool.
It solves major CDC issues like: • Data loss • Data duplication • Missed pulses • Pulse shrinking/stretching • Metastability propagation
Why Does One Need a Toggle Synchronizer?
Pulse signals face serious CDC issues:
• Pulses can be missed in fast→slow transfers • One event can appear twice • Pulse width is not preserved • 2-FF synchronizer alone cannot safely transfer pulses To fix this, we convert the pulse into something stable: Convert pulse → Toggle → Synchronize → Convert back to pulse
How Toggle Synchronizer Works ?
Step 1: The pulse toggles a flip-flop in the source domain → toggle <= ~toggle Step 2: This toggle bit is synchronized using a 2-FF synchronizer → treat the async toggle bit like a level signal Step 3: Destination detects a change in toggle → change = sync_toggle XOR prev_sync_toggle → Generate a 1-cycle clean pulse in destination domain
Why Toggle Synchronizer Is Very Reliable ?
✔ No pulse loss — event stored as a toggle ✔ No duplication — one toggle = one event ✔ No metastability propagation — isolated to FF1 ✔ No pulse width issues — toggle is a stable level ✔ Works for fast→slow clocks without missing events
When To Use a Toggle Synchronizer ?
Use it for one-shot events such as: • Interrupt pulses • DMA done / start • FIFO threshold events • Timer expiration • Control event triggers • Sensor events (temp exceeded threshold like 125C) • CPU to slow peripheral communication like USB (mouse or keyboard) Common used in SoCs with peripherals, pipelines, automotive FuSa logic.
What Problems Does Toggle Sync Solve?
CDC Issues Solved
Data Corruption✔ Yes
Data Loss✔ Yes
Data Duplication✔ Yes
Pulse Width Issues✔ Yes
Data Incoherence✘ (multi-bit issue)
Toggle Synchronizer Limitations
❌ Cannot handle back-to-back pulses (events may merge) ❌ Works only for single-bit events ❌ Destination must detect toggle change before next event
For 100% reliable event transfer use Handshake Synchronizer (see next section)
A toggle synchronizer converts a short pulse into a stable toggle, synchronizes it safely with a 2-FF chain, and recreates a clean pulse in the destination domain. This avoids data loss, duplication, and metastability issues during pulse transfer.’
Handshake Synchronizer — Guaranteed No Loss, No Duplication
From previous sections, we know 2FF synchronizer prevents data corruption for level and single bits
• 2-FF synchronizer → prevents data corruption • Toggle synchronizer → prevents data loss & duplication but fails for back-to-back pulses
Now we discuss the Handshake Synchronizer — the most reliable CDC method for control/event transfer. It guarantees: ✔ No event lost ✔ No event duplicated ✔ No overwrite ✔ Works for both directions of a fast↔slow cross clock domains ✔ Fully safe for control signals
Why Do We Need a Handshake Synchronizer?
Toggle synchronizer fails when: • Two pulses come too fast (basically back to back pulses) • New pulse arrives before destination captures previous toggle • Continuous events must be reliably transferred Handshake fixes this by enforcing: – Source cannot send the next event until destination acknowledges the previous one. This makes it 100% reliable. It does have a drawback of increased latency (time delays for secure handshake)
Two control signal are required for this to work: • REQ — Request from source clock domain to destination clock domain. • ACK — Acknowledge from destination clock domain back to source clock domain.
Sequence of operations: 1️⃣ Source sets REQ = 1 (event occurred in source clock domain) 2️⃣ Destination receives REQ 3️⃣ Destination sets ACK = 1 (to acknowledge receiving the REQ) 4️⃣ Source sees ACK and clears REQ = 0 5️⃣ Destination sees REQ cleared → clears ACK 6️⃣ System ready for next event ✔ Closed-loop ✔ No loss ✔ No duplication Both REQ and ACK cross using 2-flop synchronizers.
How it Works (Step-by-Step) ?
Step 1 — Pulse comes → Source sets REQ = 1 Step 2 — REQ crosses domain via 2-FF Step 3 — Destination sees REQ = 1 → generates event pulse → sets ACK = 1 Step 4 — ACK crosses back to REQ clock domain Step 5 — Source sees ACK → clears REQ Step 6 — Destination sees REQ=0 → clears ACK ✔ No event lost ✔ No event duplicated ✔ No metastable propagation ✔ Next event allowed only after ACK.
❌ Drawback is cannot have a short time period to process next REQ. There is overhead latency to do synchronization and then resetting REQ.
Why Handshake Synchronization Is Guaranteed Safe ?
• REQ holds event until destination confirms with ACK sent back to source clock domain • ACK ensures destination actually processed event • 2-FF sync protects both directions • No overwriting • Exactly one pulse generated • Works for both fast→slow or slow→fast due to handshake. This is the strongest CDC method for single-bit event transfer.
Where Used in Real SoCs ?
Used in: • DMA start/stop • CPU ↔ Peripheral signalling • APB–AXI bridges with different clock domains • Sensor events (e.g. temp sensor reached 125 C) • Reset release • Power-mode control • Slow→fast state transfers • Functional Safety (ISO 26262) Handshake is preferred over toggle for safety-critical paths.
Handshake Synchronization Limitations ❌ Not for multi-bit data (use FIFO) ❌ Higher latency ❌ Not for very high event rate like back to back control events Still, it is the most reliable technique for control events.
The Handshake Synchronizer uses a REQ–ACK closed-loop protocol to ensure every event is delivered exactly once across clock domains. No loss, no duplication, no overwriting — the safest CDC method for control/event transfer.
Multi-Bit CDC — Why Multi-Bit Buses Cannot Use 2-FF Synchronizers
Until now we handled single-bit CDC transfers (levels, pulses, toggles, handshakes) However, multi-bit data (control and/or real data) behaves completely differently across different clock domains.
Multi-bit transfers will cause new CDC failures to appear: • Data incoherence • Bit-skew (between bits of the transferred data0 • Partial updates • Glitches • Illegal multi-bit values (incorrect data might get transferred) • Data corruption
We will now explain why we must NOT synchronize multi-bit buses bit-by-bit using 2-FF.
Why Multi-Bit Signals Cannot Be Treated Like Single-Bit ?
A single bit is simple: only 0 or 1. But an n-bit bus has 2ⁿ possible values, and transitions do not occur simultaneously.
Example (4-bit bus): Old value: 0101 New value: 1010 Because of physical routing delays and skew, each bit flips at a slightly different moment. 💥 Destination may capture random mixed combinations like: • 0001 • 0110 • 1111 • 1011 These values never existed in the source domain → classic data incoherence.
Why 2-FF Synchronizer Fails for Multi-Bit Buses ?
If you blindly apply 2-FF synchronizer for multi-bit busses, then these events will occur in the circuit :
bus[i] → FF1[i] → FF2[i] Each bit: • Arrives at a different time • Experiences metastability differently • Settles at different speeds • May be sampled in different cycles ❌ Bits do NOT settle together ❌ Destination captures “half-old, half-new” values ❌ Bus becomes unpredictable This is the fundamental multi-bit CDC failure.
Real Example of Multi-Bit Capture Failure
Source updates from: 0x0F → 0xF0 Destination might capture: • 0x07 • 0x8F • 0xF3 • 0x70 • 0xF8 These are illegal values → can break counters, FSMs, or addresses.
Key Problems in Multi-Bit CDC
• Data Incoherence — bits sampled in different cycles • Data Corruption — one metastable bit ruins entire word • Glitches — combinational reconvergence failures • Bit-Skew — physical delays cause mismatch • Reconvergence Hazard — different logic paths settle differently These reasons are why multi-bit data must never be synced bit-by-bit.
CDC Concerns Triggered by Multi-Bit Buses
• Data Corruption — ✔ • Data Incoherence — ✔ Major issue • Data Loss — ✘ Not typical • Data Duplication — ✘ Not typical • Chip Burning — ✔ Possible if a bit goes metastable
What Is the Solution to handle Multi-Bit Busses for CDC ?
Three safe ways to transfer multi-bit data: ⭐ 1. Gray Code Synchronization For counters & FIFO pointers — only 1 bit changes per update. ⭐ 2. Multi-Bit Handshake Transfer Source freezes data, destination ACKs when received. ⭐ 3. Asynchronous FIFO For high-throughput data streams — fully CDC-safe architecture.
Gray Code Synchronization — Safest Way to Transfer Multi-Bit Counters & Pointers
Eariler it was discussed why multi-bit buses cannot use 2-FF synchronizers — because bits change at different times, causing data incoherence and illegal values. Now we will learn the most reliable solution for a special case: – When multi-bit values change only 1 bit at a time (This is where Gray Code becomes essential since it only has 1 bit changing at a time)
What is Gray Code?
Gray Code is a numbering system where only one bit changes between consecutive values. Example (3-bit): Binary: 000 → 001 → 010 → 011 → 100 → 101 → 110 → 111 Gray: 000 → 001 → 011 → 010 → 110 → 111 → 101 → 100 ✔ Binary: many bits flip ✔ Gray: exactly one bit flips
Why Gray Code Is Perfect for CDC ?
The biggest multi-bit CDC issue is: ❌ Destination may capture “mixed” data values (half old, half new) due to different time delays on the physical paths of each multi-bit
Now if only ONE bit changes for Gray Code: ✔ Even if sampled incorrectly, value is only ±1 away ✔ No illegal transitions ✔ No data incoherence ✔ System stays safe Gray Code solves multi-bit CDC cleanly.
Most Important Use Case for Gray Code — Asynchronous FIFOs
Async FIFOs use two pointers: • Read pointer → read clock domain • Write pointer → write clock domain Pointers must cross domains safely. Binary counters change multiple bits → FIFO logic will show : ❌ wrong full ❌ wrong empty ❌ wrong wrap-around → leads to data corruption
Gray Code fixes this because: ✔ Only one bit changes ✔ All values are valid ✔ Synchronization of each bit using 2-FF is safe ✔ FIFO full/empty calculations remain correct This is why all industry async FIFOs use Gray-coded pointers.
How Gray Code Synchronization Works ?
1️⃣ Source domain increments a binary counter 2️⃣ Convert binary → Gray 3️⃣ Send Gray bits across using 2-FF synchronizers 4️⃣ Destination optionally converts Gray → binary (for pointer comparison) Since only one bit can change at a time → no illegal multi-bit combinations can ever appear.
CDC Concerns Solved by Gray Code
• Data Incoherence → ✔ Fully solved • Data Corruption → ✔ Only 1 bit may change • Chip Burning → ✔ Lower metastability risk • Data Loss → ✘ Not relevant • Data Duplication → ✘ Not related
Why Gray Code Still Works During Metastability ?
If the changing Gray bit becomes metastable: Destination may capture the previous or next value. Both are valid Gray values which means FIFO logic remains correct. (There is NO unknown value captured) This is the beauty of Gray Code.
Where NOT to Use Gray Code ?
Do NOT use Gray Code for: ❌ Arbitrary multi-bit data ❌ Payload data ❌ Addresses Gray Code is ONLY for: ✔ Sequential pointers ✔ Counters ✔ Async FIFO read/write pointers
Handshake-Based Multi-Bit CDC — Guaranteed Safe Multi-Bit Transfer
In previous sections, we saw why multi-bit buses cannot use 2-FF synchronizers : resampling multi-bits in destination clock domain will result in corrupted data due to physical time delays. Additionally, we learned Gray Code works only for counters/pointers since only 1 bit changes across CDC boundary. See example block diagram below to see the microarchitecture for this multi-bit handshake logic.
However, how do we handle general multi-bit signals like: • configuration registers • status/control bundles • address + control fields • instruction words • mode settings • multi-bit command words These cannot use Gray Code (since only 1 bit changes for the counter) → so we need the safest method: ======> Handshake-Based Multi-Bit CDC
Why Multi-Bit Needs a Handshake (Not 2-FF) ?
Synchronizing each bit individually causes: • Bit skew • Mixed old+new values • Invalid combinations • Partial updates • Wrong control decisions • Glitches in logic =====> Multi-bit data must be frozen during transfer → only handshake guarantees this.
How Multi-Bit Handshake Works ?
The concept is straightforward: 1️⃣ Source freezes and captures the multi-bit data 2️⃣ Source asserts REQ = “Data ready” 3️⃣ Destination receives REQ and captures data 4️⃣ Destination asserts ACK = “Data received” 5️⃣ Source sees ACK, clears REQ 6️⃣ Destination clears ACK → ready for next transfer ✔ All REQ/ACK signals are synchronized using 2-FF ✔ Multi-bit data remains stable until transfer completes
Why This Multibit Handshake Method Is 100% Safe ?
• Source data register is held until ACK is received • Destination samples all bits coherently in one cycle • No bit-level changes during crossing • No partial updates • No illegal values • No data corruption • No duplication or loss This is the safest method for multi-bit control data.
Use cases for Multi-Bit Handshake CDC
Use it when transferring: • CPU-to-peripheral configuration • APB register updates • Mode change packets • Sensor control words • ECC/CNN config registers • DMA descriptors • Any multi-bit control bundle requiring accuracy Multi-bit handshake is used heavily in SoC control paths.
Multbit Handshake CDC Concerns Fixed
Which CDC Concerns are Fixed by Multibit Handshake ?
Data Corruption✔ Yes
Data Incoherence✔ Yes
Data Loss✔ Guaranteed Data
Duplication✔ Prevented
Chip Burning✔ Controlled (stable signals)
Limitations of Multibit Handshake
❌ Not for high-throughput data ❌ Not for continuous streaming ❌ Not for back-to-back transfers without wait (Use FIFO for these cases)
Multibit Handshake Summary
Handshake-based multi-bit CDC freezes the data until the destination acknowledges it. This guarantees no incoherence, no corruption, and perfect multi-bit accuracy across clock domains.
Asynchronous FIFO — The Ultimate CDC Solution for High-Speed Multi-Bit Data
Asynchronous FIFO (Async FIFO) is the most powerful and scalable CDC architecture.
Because it fixes the limitations of all earlier techniques ASYNC FIFO is most powerful but adds more logic and area: • 2-FF → only single-bit fix • Toggle → no back-to-back pulses (more latency and slower) • Handshake → slow & control-only • Gray Code → only for pointers
Async FIFO is the correct solution when: ⭐ Multi-bit data ⭐ High throughput ⭐ Continuous data flow ⭐ Different clock frequencies ⭐ Bursty traffic ⭐ Streaming interfaces must cross clock domains safely.
Below is a general block diagram for the Async FIFO
Why Do We Need an Async FIFO?
Other CDC techniques fail for multi-bit and high-speed transfers and back to back transfers: ❌ Multi-bit data ❌ Fast-to-slow or slow-to-fast ❌ Back-to-back transfers ❌ Continuous streaming Examples: • Handshake too slow • Toggle loses events • Gray works only for counters • 2-FF cannot sync buses Async FIFO logic solves all of these but at expense of additional logic and physical area.
What Is an Async FIFO?
It is a dual-clock (write and read clock) memory buffer that allows: • Writes in write clock domain (wclk) • Reads in read clock domain (rclk) Two independent pointers drive it: • Write pointer (wptr) for write data from memory buffer • Read pointer (rptr) for read data from memory buffer ===> Both pointers are converted to Gray Code and synchronized across domains, ensuring safe comparison.
Only pointers cross the CDC boundary — not the data. Data is stored in the memory buffer which can be dual port SRAM or flip-flops.
Why ASYNC FIFO Uses Gray Code for Pointers?
Gray code ensures only one bit changes per increment, so: ✔ No illegal pointer combinations ✔ Safe full/empty detection ✔ No multi-bit incoherence If binary counters were used: Multiple bits flip → destination may capture garbage → FIFO malfunctions.
How Async FIFO Works ?
Quick Summary : Write Side • wptr_bin increments on write operation • Bin → Gray conversion occurs on write side • Write Gray pointer crosses to read clk domain and becomes synchronized to read clk domain • Data stored at RAM address = wptr_bin Read Side • rptr_bin increments on read operation • Bin → Gray conversion occurs on read side • Read Gray pointer crosses to write clk domain and becomes synchronized to write clk domain • Data read from RAM address = rptr_bin Full / Empty Logic • FIFO Empty: wptr_gray_sync == rptr_gray • FIFO Full: specific Gray-coded pointer comparison
CDC Concerns Fixed by Async FIFO
CDC Concern Fixed by Async FIFO
Data Corruption✔ Yes
Data Incoherence✔ Yes
Data Loss✔ Yes
Data Duplication✔ Yes
Chip Burning✔ Controlled
Async FIFO is the only CDC method that solves all high-speed multi-bit CDC issues.
Where Async FIFOs Are Used in Real Chips ?
Everywhere on SoC: • AXI / AHB bridges (different clk domains) • CPU → Accelerator data pipes • Memory controllers (dram runs at 2.2GHz and internal may run at 1 GHz) • SPI/I2C data buffering (due to clock domain differences) Async FIFOs are foundational in modern SoC design.
Limitations or drawbacks of Async FIFOs
❌ More complex than 2-FF (extra functional logic overhead which takes more area) ❌ Requires dual-port SRAM (instead of a dual-port SRAM one can also use synthesizeable SRAM but this adds area penalty) ❌ Full/empty logic must be correct (need to add extra verification to check full / empty corner cases) ❌ Needs strict CDC verification (also need to ensure timing paths are constrained to not exceed a clock period delay)
It is the most reliable and reusable CDC architecture used in modern SoCs.
Pulse vs Toggle vs Handshake vs FIFO — Which CDC Technique Should You Use?
General topics covered : 2-FF, pulse sync, toggle, handshake, Gray code, multi-bit handshake, and async FIFO
What Are You Transferring?
All CDC signals fall into one of these: • Single-bit LEVEL (status, enable, mode, fifo full, fifo empty) • Single-bit PULSE (interrupt, event_done) • Multi-bit DATA (registers, pointers, payload) Each category requires a different CDC solution.
Quick Review of the CDC Techniques
Industry decision matrix for CDC methods/techniques
Single-Bit LEVEL Signals → Use 2-FF Synchronizer
Examples: • enable • mode • status • reset release ✔ Best choice → 2-FF synchronizer ✘ Don’t use toggle, handshake, or FIFO.
Single-Bit PULSE Signals → Toggle or Handshake
Examples: • interrupt • timer expiration (timeout) • dma_req • error pulse ✔ Pulse rate is low → Toggle Synchronizer ✔ Must NEVER lose a pulse → Handshake Synchronizer ✔ Slow→fast domain → Pulse Stretching ✘ Don’t use 2-FF for pulses.
Multi-Bit Data → Choose Based on Behavior
Examples: • config registers • address buses • mode fields • FIFO pointers • sensor data ✔ Counters / pointers → Gray Code ✔ Low-rate multi-bit control → Multi-bit handshake ✔ High-speed continuous data → Async FIFO ✘ Never sync each bit with 2-FF individually.
Quick CDC Decision Guide
Is it multi-bit? • Yes → Gray code / Handshake / FIFO • No → Continue Is it a level or pulse? • Level → 2-FF • Pulse → Toggle / Handshake / Pulse stretch
Each CDC technique exists to solve a specific problem: • 2-FF → single-bit levels • Toggle → pulses (loss + duplication fixed) • Handshake → guaranteed event delivery • Gray code → counters & pointers • Multi-bit handshake → multi-bit control • Async FIFO → high-speed multi-bit streams Choosing the right method is key to building reliable SoCs.
CDC Divergence — Why Async Signals Must Not Fan Out into Multiple Sync Paths
CDC Divergence is a structural CDC hazard that occurs when one asynchronous signal fans out into multiple synchronizers or logic paths in the destination domain. This is one of the most common and dangerous CDC design mistakes. It can lead to FSM and Interfaces operating incorrectly.
What Is CDC Divergence?
CDC Divergence happens when: • One async signal → goes to → multiple synchronizers • One async signal → feeds → multiple logic paths Example:
┌──> [2-FF Sync A] → Logic A Async Signal —┤ └──> [2-FF Sync B] → Logic B Each path samples and resolves metastability independently, leading to mismatched timing.
Why CDC Divergence Is Dangerous ?
Each synchronizer may: • Resolve metastability at a different time • Capture the async signal in different cycles • Drive logic with inconsistent values This causes: ❌ Glitches ❌ False pulses ❌ Missed events ❌ Conflicting FSM transitions ❌ Broken protocols
Real Example: CDC Divergent IRQ Synchronization
irq_async → Sync_A → FSM A irq_async → Sync_B → FSM B Scenario: • FSM A sees the interrupt earlier • FSM B sees it later ➡ One FSM begins processing while the other doesn’t → illegal states, wrong sequences, system malfunction.
Glitch Example (Reconvergence Failure)
Async → Sync1 → AND —->– OUT (glitch) Async → Sync2 → NOT —- If Sync1 and Sync2 settle in different cycles → output glitches, causing: • Spurious enable • False event trigger • Power spikes • Data corruption
Even with 2-FF chains: • Each chain resolves metastability differently • Delays and skew differ • Local placement affects timing Therefore: ❌ Multiple synchronizers = multiple interpretations of the same async event ❌ Always unsafe
Industry Rule to avoid CDC Reconvergence : One Async Signal → One Synchronizer Only
Correct approach:
Async → [Single 2-FF Sync] → Synced_Signal │ ├──→ Logic A ├──→ Logic B └──→ Logic C ✔ All logic receives the same clean signal ✔ No mismatched timing ✔ No divergence hazards
Common Places Where CDC Divergence Happens
You’ll often find divergence issues in: • Interrupt lines • Reset deassertion • Gating enables • State machine triggers • Control signals shared between blocks CDC tools frequently flag these as structural violations.
CDC Concerns Triggered by Divergence
CDC ConcernTriggered?
Data Corruption✔ Yes
Data Incoherence✔ Yes
Data Loss✔ Yes
Data Duplication✔ Yes
Chip Burning✔ Yes (glitch-induced currents)
CDC Convergence — When Multiple Async Signals Enter the Same Logic & Cause Glitches
CDC Convergence occurs when two or more asynchronous signals (or separately synchronized versions of them) feed into the same destination logic. This is one of the biggest sources of: • Glitches • False pulses • Incorrect state transitions • Unpredictable combinational outputs
What Is CDC Convergence?
Convergence happens when multiple signals from another clock domain enter the same logic block:
async_A → Sync_A —-AND/OR/MUX → Output async_B → Sync_B —- Each synchronized signal settles at different cycles due to: • Different metastability resolution • Different routing delays • Clock skew • Physical placement differences This leads to inconsistent logic behavior.
Why Convergence Causes Problems ?
If two signals converge into a logic gate: ✔ One may stabilize earlier ✔ The other one may settle later The logic gate temporarily sees wrong combinations, creating: ❌ Glitches ❌ False transitions ❌ False interrupts ❌ Invalid FSM decisions
Classic Glitch Example (Most Common)
If Sync_A updates at time T and Sync_B updates at time T+1
a signal ABORT at time T + 0.5000 causes an UNEXPECTED GLITCH at time T+0.5000 This 1-cycle glitch can: • Fire a false interrupt • Clock a register incorrectly • Trigger unwanted FSM transitions
Why Metastability Makes It Worse ?
Even with 2-FF synchronizers: • Sync_A may resolve metastability after 1 cycle • Sync_B may resolve after 2 cycles This mismatch increases glitch probability.
Another Real-World Glitch Example for Convergence
Two control signals entering a MUX selector:
sel_A → Sync_A sel_B → Sync_B mux_out = (sel_A & data1) | (sel_B & data2) If synced signals change at slightly different times: ✔ MUX may briefly choose the wrong data ✔ Temporary corruption occurs ✔ Extremely hard to debug on silicon
How to Avoid Convergence Hazards ?
✔ Solution 1 — Combine Before Syncing Perform logic operation(s) in source domain and synchronize before sending over to destination clk domain:
combined_async = async_A OR async_B combined_async → one synchronizer → safe_out ✔ Solution 2 — Use Multi-Bit Handshake Freeze entire data bundle → capture → ACK → release. ✔ Solution 3 — Use Source-Domain FSM Do decisions in source domain; send only the final event. ✔ Solution 4 — Sync Once, Then Fan Out Never sync operands separately. ✔ Solution 5 — Register Logic in Destination Avoid raw combinational logic fed by multiple CDC inputs.
CDC Concerns Triggered by Convergence
Concern Triggered?
Data Corruption✔
Data Incoherence✔
Data Loss✔
Data Duplication✔
Chip Burning✔ (due to glitch-induced short currents)
CDC Reconvergence — The Most Dangerous Structural CDC Hazard
CDC Reconvergence is a silent CDC killer. It occurs when the same async signal is synchronized through multiple paths and later recombined in destination logic. This leads to glitches, false pulses, mismatched decisions, and extremely hard-to-debug silicon failures.
What Is CDC Reconvergence?
┌──→ Sync A —-\ Async Signal —–┤ AND/OR → OUT └──→ Sync B —-/ Each synchronizer behaves differently → the same signal appears as different values at the same time.
This is different than convergence as this case has the async signal synchronized in several places and “recombined” later. As we have studied earlier, metastability can cause synchronization to occur at different times leading to incorrect logic signals when sampling in same destination clock domain.
Why Reconvergence Is Dangerous
Because each sync chain resolves metastability on a different cycle: • Sync A → updates earlier • Sync B → updates later This results in: ❌ Glitches ❌ False triggers ❌ Wrong FSM transitions ❌ Incorrect control decisions
Classic Glitch Example for Reconvergence
async → Sync_A –\ OR → out async → Sync_B –/ If Sync_A updates at T and Sync_B at time T+1,
OR output briefly goes LOW → a false pulse.
A one-cycle glitch can: • Fire an interrupt • Clock a register • Corrupt a data path • Move an FSM to an invalid state
Most Common Bug Found in Reconvergence : Inverted + Non-Inverted Paths
async → Sync1 → out1 async → NOT → Sync2 → out2 logic = out1 AND out2 Different delays + inversion → guaranteed glitch. CDC tools flag this as high severity.
How to Fix Reconvergence ?
✔ Synchronize once → fan out the synced signal ✔ Perform logic operations in source domain before crossing (with source clock domain register) ✔ Use multi-bit handshake for grouped signals ✔ Avoid multiple sync paths of the same signal ✔ Use safe encodings (Gray/one-hot) when needed
CDC Reconvergence happens when the same async signal crosses through multiple separate sync paths and then recombines. Because each path stabilizes differently, reconvergence creates glitches, false events, and unpredictable behavior. Always perform logical operation in source clk domain and synchronize once in source clk domain and fan out the synchronized output in destination clk domain.
Reset Domain Crossing (RDC) — What, Why, When, Where
After CDC data and control paths, the next critical topic is Reset Domain Crossing (RDC) which occurs in every SoC. Many designs fail not because of data CDC — but because of bad reset release (aka reset deassert).
What Is Reset Domain Crossing (RDC)?
RDC occurs when a reset signal crosses clock domains. Typically: • Reset is generated externally or in one source clk domain • Used by multiple clock domains • Often asynchronous to destination clocks NOTE : Reset assertion and deassertion behave very differently.
Why Is RDC Dangerous for SoC ?
✔ Reset assertion (active reset): usually safe due to large setup time ❌ Reset deassertion (release): very dangerous if async bcause hold time to flops can be violated If reset is released near a clock edge: • Flops exit reset in different cycles • Some flops may go metastable • FSMs start in illegal states • Counters, handshakes, FIFOs misalign Result: ❌ Unpredictable boot behavior ❌ Rare, silicon-only failures ❌ Bugs that escape STA & simulation
What Goes Wrong in Bad Reset Release?
• Partial reset of logic • FSM bits not aligned (incorrect FSM reset state) • Counters start incorrectly • CDC paths break at startup • Control logic behaves randomly (interfaces may lockup and not operate) This is one of the hardest bugs to debug in real chips.
When & Where RDC Violations Occur ?
RDC exists whenever: • One reset drives multiple clock domains (reset delay skew will cause some logic to reset earlier or later than others) • Reset comes from external pin / PMU • POR, warm reset, watchdog reset • Reset enters clock-gated logic • Reset crosses power domains • Reset feeds CDC synchronizers or FIFOs ➡ In modern SoCs, RDC is unavoidable.
Golden Rule of GOOD Reset Design
Async Assert, Sync Deassert • Assert reset asynchronously → fast & safe • Deassert reset synchronously → clean & aligned This rule exists only to prevent RDC failures.
Safe Reset Concept (High-Level)
Wrong:
always_ff @(posedge clk or negedge rst_n) (Async deassert → unsafe)
Correct idea:
rst_async → reset synchronizer → rst_sync_n Use rst_sync inside the clock domain so all flops exit reset together.
Reset Domain Crossing (RDC) happens when reset signals cross clock domains. While reset assertion is usually safe, asynchronous reset deassertion can break the entire design through partial reset and illegal states. The golden rule: Async assert, sync deassert.
How do we design reset logic correctly in multi-clock SoC systems?
The Golden Rule of Reset Design : Async Assert, Sync Deassert
• Reset assertion must be immediate (force flip-flops to known states immediately) • Reset deassertion must be clock-aligned (must satisfy flip-flop reset hold times) Breaking this rule is the #1 cause of RDC failures.
Why Reset Deassertion Must Be Synchronized ?
If reset is released asynchronously: • Some flops exit reset earlier • Some exit later • Some go metastable Result: ❌ FSM illegal states ❌ Counters start wrong ❌ CDC handshakes break ❌ FIFO pointers misalign
❌ Interfaces can lock up and not respond
So reset release must behave like a CDC control signal.
✔ Both FFs clocked by destination clock ✔ Reset asserts immediately ✔ Reset releases after 2 clean clock edges ✔ All flops exit reset together
One Reset Doesn’t Mean One Synchronizer
Important rule:
Global Reset ├─→ Reset Sync (clk_A) ├─→ Reset Sync (clk_B) └─→ Reset Sync (clk_C) ✔ Each clock domain needs its own reset synchronizer ❌ Never share a synchronized reset across domains
Reset + Clock Gating (Common Bug)
❌ Reset released while clock is gated OFF → Some flops never see reset deassertion ✔ Ensure clock is ungated during reset release ✔ Or release reset before enabling clock gating
Reset in CDC Structures (Critical)
• 2-FF sync → both FFs reset identically • Toggle / Handshake → reset protocol to idle • Async FIFO → – write reset resets write pointer – read reset resets read pointer – FIFO must start EMPTY Bad FIFO reset = silent data corruption.
Common RDC Mistakes
❌ Using async reset everywhere ❌ Sharing reset sync across domains ❌ Reset used as control/data ❌ Reset released under gated clock ❌ Reset after combinational logic All are high-severity RDC violations.
RDC Summary
Safe reset design follows one rule: Asynchronous assert, synchronous deassert — per clock domain. Using a local reset synchronizer in each clock domain ensures clean startup, prevents metastability, and avoids partial reset failures. RDC correctness is as critical as CDC correctness.
Reset Domain Crossing (RDC) — A Common Interview & Design Question
This is one of the most frequently asked RDC questions in interviews and a real design decision engineers must get right.
Interview Question When a reset crosses clock domains, we use a 2-FF reset synchronizer. But those two flip-flops themselves need a reset.
Which reset should be used for the 2-FF synchronizer flops?
✅ Correct Answer Use the SAME asynchronous reset that is being synchronized to reset the 2-FF synchronizer flops. This is intentional, safe, and industry-standard. 🔸 Why This Is Correct (Key Reasoning) • Reset often comes from POR / PMU / external pin → asynchronous • Reset assertion must be immediate, regardless of clock • Reset deassertion must be clean and clock-aligned The 2-FF synchronizer’s job is only to make reset deassertion synchronous. So: • Both synchronizer FFs – are clocked by the destination clock – use the raw async reset on their reset pins • Output of FF2 becomes rst_sync • rst_sync is used by all logic in that clock domain This guarantees: ✔ Async assert ✔ Sync deassert 🔸 Critical Design Rules (Interview Gold) ❌ Never reset the synchronizer flops using the synchronized reset → This creates a chicken-and-egg problem. ✔ One reset synchronizer per clock domain ❌ Never reuse a reset synchronized to clk_A in clk_B ✔ Only the reset synchronizer sees async reset All other logic must use only the synchronized reset 🔸 Correct Reset Structure (Conceptual)
rst_async | [FF1] ← async reset, clk = dest_clk | [FF2] ← async reset, clk = dest_clk | rst_sync → used by all logic in this domain
🔸 Key Takeaway ⭐ Reset assertion is asynchronous ⭐ Reset deassertion must be synchronous That is exactly why the reset synchronizer flops are reset using the same async reset they synchronize.
🔸 Why This Question Matters for RDC ? • Very common CDC/RDC interview question • Flagged by SpyGlass RDC • Critical for FSM stability • Mandatory for ISO 26262 designs • Wrong design causes rare silicon-only failures
Reset Domain Crossing (RDC) — Real Failure Scenarios & Debugging
RDC bugs are among the hardest failures to debug because they often: • Do not appear in simulation • Fail only occasionally • Appear only on silicon • Are highly timing-dependent
Below are real RDC failures seen in industry.
Partial Reset Release Failure
❌ Problem Reset deasserted asynchronously → flops exit reset in different cycles. 🔥 Symptoms • FSM illegal states • Random register values • Boot sometimes passes, sometimes fails 🔍 Root Cause No reset synchronizer. ✅ Fix Async assert, sync deassert. One reset synchronizer per clock domain.
Reset Used as Data / Enable Failure
❌ Problem Reset reused as enable, qualifier, or control. 🔥 Symptoms • Glitches during reset release • Counters increment in reset • Unexpected enables, power spikes 🔍 Root Cause Reset treated as functional signal. ✅ Fix Reset only resets. Generate separate synchronized control signals.
Shared Reset Synchronizer Across Domains Failure
❌ Problem One synchronized reset reused for multiple clocks. 🔥 Symptoms • One domain boots correctly • Another behaves randomly • Handshakes fail at startup 🔍 Root Cause Reset synced to clk_A used in clk_B. ✅ Fix One reset synchronizer per clock domain.
Reset Released While Clock Is Gated Failure
❌ Problem Reset released when clock is OFF. 🔥 Symptoms • Block stuck in reset • Logic never activates 🔍 Root Cause Clock gating active during reset release. ✅ Fix Ungate clock during reset release or release reset before clock gating.
Async FIFO Reset Misalignment Failure
❌ Problem Read/write resets not coordinated and possibly reset with incorrect clock domain(s). 🔥 Symptoms • FIFO not EMPTY after reset • Invalid first data 🔍 Root Cause Pointers not reset per domain. ✅ Fix Reset write pointer in write domain Reset read pointer in read domain Verify EMPTY/FULL after reset.
Reset Reconvergence Failure
❌ Problem Reset synchronized multiple times and reconverges. 🔥 Symptoms • Reset glitches • False reset pulses • Random reinitialization 🔍 Root Cause Multiple reset sync paths. ✅ Fix Single reset synchronizer per domain. Fan out only the synchronized reset.
✔ CDC/RDC tools (SpyGlass RDC) • Async deassert checks • Reset fanout & reconvergence • Reset + clock gating issues ✔ Silicon bring-up • Boot loops • Power cycling • Temp / voltage stress ✔ Waveform analysis • Reset vs clock • FSM state after reset • FIFO pointers
RDC Key Takeaway
RDC failures are rare, system-wide, and catastrophic. Correct RDC design —> async assert, sync deassert, one reset synchronizer per domain — is non-negotiable and golden standard.
SOC design with CDC
As an SOC may easily have ten or more clock domains one may easily overlook connections in the RTL code (Verilog/Systemverilog). Thus, it’s the role of CDC to catch these escape errors or bugs that occur during the development stage and not let it propagate it into simulation or real silicon.
CDC Divergence — Why Async Signals Must Not Fan Out into Multiple Sync Paths
✔ Solution 1 — Combine Before Syncing Perform logic operation(s) in source domain and synchronize before sending over to destination clk domain:
combined_async = async_A OR async_B combined_async → one synchronizer → safe_out ✔ Solution 2 — Use Multi-Bit Handshake Freeze entire data bundle → capture → ACK → release. ✔ Solution 3 — Use Source-Domain FSM Do decisions in source domain; send only the final event. ✔ Solution 4 — Sync Once, Then Fan Out Never sync operands separately. ✔ Solution 5 — Register Logic in Destination Avoid raw combinational logic fed by multiple CDC inputs.
CDC Reconvergence – Why Async Signals Must Not Fan Out and Fan back to One Path
CDC Reconvergence happens when the same async signal crosses through multiple separate sync paths and then recombines. Because each path stabilizes differently, reconvergence creates glitches, false events, and unpredictable behavior. Always perform logical operation in source clk domain and synchronize once in source clk domain and fan out the synchronized output in destination clk domain.
SOC design with RDC
RDC (reset domain crossing) is a special case of CDC applied to resets.
RDC failures are rare, system-wide, and catastrophic. Correct RDC design —> async assert, sync deassert, one reset synchronizer per domain — is non-negotiable and golden standard.
Sampling refers to the rate of sampling an analog signal. In order to reconstruct an analog signal in the digital domain, one needs to operate the digital frequency at least twice the analog frequency to maintain correct capture of the analog signal. This is also called the Nyquist frequency. So for example, if the analog signal like audio frequency is 64kHz, the minimum digital frequency would be 128kHz to correctly capture this audio signal. The diagram below describes why one needs twice the frequency.
Quantization errors occur when converting analog signals into discrete digital values. Because digital systems have limited precision, continuous values must be rounded or truncated to the nearest available bit level. This inherent mismatch creates distortion and adds a “noise floor” to the processed signal.
Types of Quantization Errors
Input/Output Quantization: The initial loss of precision when an Analog-to-Digital Converter (ADC) maps continuous voltages to discrete binary levels.
Coefficient Quantization: In digital filters, the ideal filter coefficients (calculated with floating-point math) must be rounded to fit fixed-point hardware registers.
Product Round-off & Overflow: Mathematical operations like multiplication and addition expand the bit length of a signal, often requiring rounding or truncation between processing stages.
Key Impacts
Quantization Noise: Appears as a wideband noise (e.g., hiss in audio) or harmonic distortion, which limits the system’s dynamic range.
Limit Cycles: In recursive (IIR) filters, round-off errors can trap the output signal in persistent, low-level oscillations, even when the input is zero
How to Minimize the Errors
Increase Bit Depth: Moving from an 8-bit to a 16-bit or 24-bit system increases the number of available quantization levels, significantly reducing the step size and error. [1, 2]
Dithering: Adding a tiny, controlled amount of random noise to the analog signal prior to quantization disrupts the correlation between the signal and the error. This converts harsh, deterministic distortion into benign white noise.
Noise Shaping: A technique used in oversampling converters (like Delta-Sigma ADCs) that pushes quantization noise out of the frequencies of interest and into higher, inaudible ranges.
SNR is the metric used across science and engineering that compares the power of a desired signal versus the level of background noise (unwanted interference). A larger or higher SNR generally indicates a clearer more accurate and more reliable signal.
How SNR works:
Concept: It measures the separation between the legitimate signal (like a singer’s voice or a data transmission) and the noise floor (like electrical hum or static).
The Math: SNR is calculated as the ratio of signal power to noise power. Because this ratio can be very large or very small, it is typically expressed in decibels (dB).
General Rule: An SNR greater than \(0\text{ dB}\) (or a ratio higher than \(1:1\)) means the desired signal is stronger than the background noise. [1, 2]
Typical Applications
Audio & Music: A high SNR (e.g., \(95\text{ dB}\) or higher) in an amplifier or microphone means the music or vocals will be crisp and clear, with minimal background hiss.
Wireless & Networking: In Wi-Fi or cellular networks, an SNR of at least \(25\text{ dB}\) is typically required for good, reliable connectivity, while anything below \(10\text{ dB}\) usually results in a poor or dropped connection.
Imaging: In photography and sensor technology, a higher SNR results in sharper images with less visual “grain” or noise.
Equation
SNR (dB) = Signal (in dB) – N (in dB)
How the FFT Works
Time to Frequency Conversion: A standard Discrete Fourier Transform (DFT) is mathematically intensive. The FFT is a fast, computationally optimized method to calculate the DFT, changing the complexity from (O(N^2)) to (O(N log N)).
Decomposition: The algorithm breaks an \(N\)-point time-domain signal into shorter segments, calculates the spectrum for each, and synthesizes them using a “butterfly” computational element.
Output Data: The output consists of complex numbers that define both the amplitude (magnitude) and phase of every frequency component within the signal.
Key Applications
Audio & Acoustics: Used heavily for audio compression, noise reduction, and analyzing vocal or musical signals.
Electronics & Communications: Helps troubleshoot noise, harmonic distortion, and signal interference in oscilloscopes and spectrum analyzers.
Vibration & Structural Analysis: Detects wear or imbalances in mechanical machinery by identifying dominant frequency spikes.
Common Processing Considerations
Sampling Rate: Per the Nyquist theorem, your sampling frequency must be at least twice the maximum frequency you want to measure to avoid aliasing.
Spectral Leakage: Because signals are observed over finite time windows, boundaries can create artificial frequency components. Windowing functions (like Hamming or Hanning) are often applied prior to the FFT to minimize this.
THD (Total Harmonic Distortion) in signal processing is a metric used to measure the amount of unwanted harmonic distortion in a signal relative to its original harmonic frequency. It evaluates how much an output waveform has deviated from the ideal input waveform due to non-linearities in a system.
A lower THD indicates a more accurate and higher-fidelity signal, while a higher THD means the signal has been more heavily altered or distorted.
How THD is Calculated
Mathematically, THD is defined as the ratio of the total power (or root mean square, RMS) of all the harmonic frequencies combined to the power (or RMS) of the fundamental frequency.
(V1) is the RMS voltage (or current) of the fundamental frequency.
(V2, V3, V4), etc., are the RMS voltages (or currents) of the subsequent harmonics.
THD is typically expressed as a percentage (e.g., (0.01%)) or in decibels (dB) (e.g., (-80dB).
Where THD Matters
Audio Systems: In speakers, microphones, and amplifiers, a low THD guarantees the equipment reproduces music or voice accurately. An ideal audio amplifier aims to have a THD close to zero, whereas a THD over a few percent might be noticeable to the listener.
Combination logic using continuous assignment example
FSM to detect serial 10110
FSM : Traffic Controller
FSM : Round Robin Arbiter (used for QoS)
IEEE Floating Point : Single Precision
FIFO Depth Calculation
Systemverilog Asstion to check analog connections
Systemverilog Synchronous FIFO
Systemverilog Asynchronous FIFO
Rate Adapter using Valid/Ready Handshake
Simple data flop flop example
module dff (data, clock, q);
input data;
input clock;
output q:
reg q;
always @(clock)
q <= data;
end
endmodule
Combinational logic using continuous assignment example
Think of what decoding you want like an address decoding for all bits to 0 and then create logic 1
assign address_dec = (address[3:0] == 4’b0000) 1 ? 0 ; //output 1 when all address bits are 0 else output 0
FSM (finite state machine) example : serial bit stream algorithm to detect start with “1” 10110 and end with “0”.
Step 1: Define states and transitions
Start with reset which becomes State 0 (S0)
S0 : If data == 0 then loop back to S0
else if data == 1 then transition to S1 [1]
S1 [1] : if data == 0 then transition to S2 (detected pattern [10])
else if data == 1 then loop back to S1 (detected a [1])
S2 [10] : if data == 0 then transition to S0 (detected 100 pattern which doesn’t exist so start all over a wait for a 1 input)
else if data == 1 then transition to S3 (detected pattern [101])
S3 [101] : if data == 0 then transition to S2 (detected 1010 pattern which is basically [10])
else if data == 1 then transition to S4 (detected pattern [1011])
S4 [1011] : if data == 0 then transition to S2 (possible [10] pattern) (detected pattern [10110]) output DETECT signal
else if data ==1 then transition to S1 (detected [1] pattern)
Step 2 – Convert FSM to verilog
to be added
FSM example : traffic controller algorithm
Background context : Traffice controller controls the main highway which has a green light by default since it’s the main thoroughfare. The perpendicular road aka farm road requires any crossing traffic to be detected by a “sensor”. In the meantime, it must wait at the farm red light. It is assumed the output for each set of lights : highway and farm is driving a set of 3 lights : red, yellow, green. An active high drives the light for each light. We can add assertions that only one light is active for each direction : highway and farm. There can never be more than one active high signal for each output.
Step 1 – Define states and transition states // traffic controller // assume red,yellow,green light for highway East/West // this becomes the out_fast[2:0] = red,yellow,green // assume red,yellow,green light for road North/South // this becomes the out_slow[2:0] = red,yellow,green // assume highway has priority and always green // assume sensor is on road North and South side to detect car
// 6 possible states but can be reduced // fast_green // fast_yellow // fast_red // slow_green // slow_yellow // slow_red
// or since fast_green means slow_red // we can encode the states into 4 instead of 6 states // assume fast_green and slow_red = 2’b00 // assume fast_yellow and slow_red = 2’b01 // assume fast_red and slow_green = 2’b10 // assume fast_red and slow_yellow = 2’b11 parameter FGREEN_SRED = 2’b00; parameter FYELLOW_SRED = 2’b01; parameter FRED_SGREEN = 2’b10; parameter FRED_SYELLOW = 2’b11;
reg [1:0] current_state, next_state;
// next state always @(*) begin case(current_state) FGREEN_SRED: begin out_fast = 3’b001; // green highway out_slow = 3’b100; // red farm if (sensor) // sensor detects vehicle on farm road then turn light on highway to yellow next_state = FYELLOW_SRED; else next_state = FGREEN_SRED; // keep green highway and red farm end FYELLOW_SRED: begin out_fast = 3’b010; // yellow highway out_slow = 3’b100; // red farm // change to next state of Fast red and slow green next_state = FRED_SGREEN; end FRED_SGREEN: begin out_fast = 3’b100; // red highway out_slow = 3’b001; // green farm // change to next state of Fast red and slow yellow next_state = FRED_SYELLOW; end FRED_SYELLOW: begin out_fast = 3’b100; // red highway out_slow = 3’b010; // yellow farm // change to next state of fast green and slow red next_state = FGREEN_SRED; end default: next_state = FGREEN_SRED; endcase end
// current state
always @(posedge clk or posedge reset) begin if (reset) current_state <= 3’b000; else current_state <= next_state; end
2. Convert 0 10000001 11001100000000000000000 to decimal real number
1. Convert exponent to decimal = 129
2. Convert Excess to Exponent = 129 – 127 = 2
3. Convert to exponent notation = 1.110011 x 22
4. Remove exponent = 111.0011
5. Convert to decimal real number = 7.1875
Floating Point Number Representation
Floating point numbers are used to represent noninteger fractional numbers and are used in most engineering and technical calculations, for example, 3.256, 2.1, and 0.0036. The most commonly used floating point standard is the IEEE standard. According to this standard, floating point numbers are represented with 32 bits (single precision) or 64 bits (double precision).
In this section, we will look at the format of 32-bit floating point numbers only and see how mathematical operations can be performed with such numbers.
According to the IEEE standard, 32-bit floating point numbers are represented as follows:
The most significant bit indicates sign of the number, where 0 indicates positive and 1 indicates negative.
The 8-bit exponent shows the power of the number. To make the calculations easy, the sign of the exponent is not shown, but instead excess 128 numbering system is used. Thus, to find the real exponent, we have to subtract 127 from the given exponent. For example, if the mantissa is “10000000,” the real value of the mantissa is 128 − 127 = 1.
The mantissa is 23 bits wide and represents the increasing negative powers of 2. For example, if we assume that the mantissa is “1110000000000000000000,” the value of this mantissa is calculated as follows: 2−1 + 2−2 + 2−3 = 7/8.
The decimal equivalent of a floating point number can be calculated using the following formula:
Number=(−1)s 2**(e−127) 1⋅f,where s=0 for positive numbers, s=1 for negative numbers,e=exponent (between 0 and 255), and f=mantissa.
As shown in the above formula, there is a hidden “1” before the mantissa; i.e., mantissa is shown as “1 · f.”
The largest and the smallest numbers in 32-bit floating point format are as follows:
0 11111110 11111111111111111111111
This number is (2 − 2−23) 2127 or decimal 3.403 × 1038. The numbers keep their precision up to six digits after the decimal point.
0 00000001 00000000000000000000000
This number is 2−126 or decimal 1.175 × 10−38.
4bitALU example
to be added
Signed adder with two different bit lengths example
to be added
4×4 unsigned Multiplier example
to be added
4×4 signed multiplier example
to be added
FIFO depth calculation
How deep does a FIFO need to be to prevent overflow or underflow?
This scheme is based upon maximum INGRESS (input) rate and maximum OUTGRESS (drain) rate
First establish the maximum INGRESS (input) rate
Second, determine the maximum OUTGRESS (drain) rate
Third, the difference between INGRESS – OUTGRESS becomes the FIFO DEPTH needed to prevent overflow.
INGRESS : can be interpreted as maximum write burst
OUTGRESS : can be interpreted as ((maximum write burst size) x write clk period ) / read clk period
GIVEN : Input FREQ A is 1/4 Output FREQ B
Period B enable = Period A enable * 100 (this becomes the BURST LENGTH)
or turned around 100 Period A enables are bursting to become one Period B enable
Duty Cycle B = 25% (this means reading is performed 1/4 of total burst time)
Assuming clk_B = 100 MHz or 10ns period, then clk_A = 100MHz/4 = 25 MHz or 40ns
So total write BURST time = 40 ns * 100 = 4000 ns
So total read BURST time = 1/4 of total write BURST time = 4000/4 = 1000 ns
So FIFO should be able to hold for 4000 – 1000 = 3000 ns
So number of data items can be read in a period of 3000 ns = 3000 ns/40ns = 75
So minimum depth of the FIFO should be 75.
SYSTEMVERILOG ASSERTION TO FORMALLY CHECK CONNECTIONS
always_comb
con_check: assert #0 ianalog.bity==ireg.bitx; else $error(“Connectivity from register bit x to analog input bity is not satisfied”);
FIFO synchronous example
This design parameters both the Data Width (DATA_WIDTH) and the FIFO Depth (FIFO_DEPTH). It automatically calculates the required address pointer width using $clog2, handles full/empty flags, and prevents data corruption from overflow or underflow.
Example fifo block diagram and sample waveform is shown below from Gemini generation
`timescale 1ns / 1ps
module sync_fifo #( parameter int DATA_WIDTH = 8, // Width of the data bus parameter int FIFO_DEPTH = 16 // Depth of the FIFO (should be power of 2) )( input logic clk, // Clock signal input logic rst_n, // Active-low synchronous reset
// Write Interface
input logic wr_en, // Write enable
input logic [DATA_WIDTH-1:0] wr_data, // Data to be written
// Read Interface
input logic rd_en, // Read enable
output logic [DATA_WIDTH-1:0] rd_data, // Data to be read
// Status Flags
output logic full, // FIFO is full
output logic empty // FIFO is empty
);
// Calculate pointer width. Extra bit is used to distinguish between Full and Empty.
localparam int ADDR_WIDTH = $clog2(FIFO_DEPTH);
// Memory array (Inferred RAM)
logic [DATA_WIDTH-1:0] fifo_mem [0:FIFO_DEPTH-1];
// Read and Write Pointers (include an extra MSB bit for wrap-around detection)
logic [ADDR_WIDTH:0] wr_ptr;
logic [ADDR_WIDTH:0] rd_ptr;
// --- Status Flags Logic ---
// Empty when pointers are completely identical
assign empty = (wr_ptr == rd_ptr);
// Full when the lower address bits match, but the MSB (wrap-around bit) is different
assign full = (wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0]) &&
(wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]);
// --- Write Operation ---
always_ff @(posedge clk) begin
if (!rst_n) begin
wr_ptr <= '0;
end else if (wr_en && !full) begin
fifo_mem[wr_ptr[ADDR_WIDTH-1:0]] <= wr_data;
wr_ptr <= wr_ptr + 1'b1;
end
end
// --- Read Operation ---
always_ff @(posedge clk) begin
if (!rst_n) begin
rd_ptr <= '0;
rd_data <= '0;
end else if (rd_en && !empty) begin
rd_data <= fifo_mem[rd_ptr[ADDR_WIDTH-1:0]];
rd_ptr <= rd_ptr + 1'b1;
end
end
endmodule
Architectural Highlights
Wrap-Around Pointer Method: Instead of keeping track of a continuous counter (count == FIFO_DEPTH), this design uses pointers that are 1 bit wider than needed for the address space (ADDR_WIDTH + 1).
If wr_ptr and rd_ptr are exactly equal, the FIFO is empty.
If wr_ptr has looped around (MSB is different) but the lower indices match rd_ptr, the FIFO is full.
Safety Guards: The write logic (wr_en && !full) and read logic (rd_en && !empty) ensure that accidental assertions of read/write enables during boundary states are ignored, preventing data corruption.
Inferred Memory: The fifo_mem structure is written cleanly so modern synthesis tools (like Vivado or Quartus) can easily map it directly to block RAM (BRAM) or distributed RAM depending on the sizing.
Example Testbench Module
Here is a simple testbench code snippet to demonstrate how you instantiate this module with custom parameters:
module tb_sync_fifo; // Instantiate with custom width (16-bit) and depth (8 slots) localparam int W = 16; localparam int D = 8;
logic clk;
logic rst_n;
logic wr_en;
logic [W-1:0] wr_data;
logic rd_en;
logic [W-1:0] rd_data;
logic full;
logic empty;
// UUT Instance
sync_fifo #(.DATA_WIDTH(W), .FIFO_DEPTH(D)) uut (.*);
// Clock Generator (100MHz)
always #5 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
wr_en = 0;
rd_en = 0;
wr_data = 0;
#20 rst_n = 1; // Release reset
// Write Data to FIFO
@(posedge clk);
wr_en = 1; wr_data = 16'hAAAA;
@(posedge clk);
wr_data = 16'hBBBB;
@(posedge clk);
wr_en = 0;
// Read Data from FIFO
@(posedge clk);
rd_en = 1;
@(posedge clk);
rd_en = 0;
#50 $finish;
end
endmodule
FIFO asynchronous example
Here is a complete, robust, and fully synthesizable Asynchronous FIFO implementation in SystemVerilog.
Here is the detailed block diagram illustrating the core architecture of an Asynchronous FIFO, alongside a timing waveform that demonstrates critical data transfer operations across independent clock domains.
1. Asynchronous FIFO Block Diagram
The block diagram shows how data moves between a faster Write Domain (100MHz) and a slightly slower, independent Read Domain (66.67MHz). The vital feature of this architecture is how the pointers cross the domains safely:
Dual-Port Memory: The MEM ARRAY is the central storage, written by waddr and read by raddr.
Cross-Domain Synchronization (Gray Code): Notice the binary-to-Gray converters. Because standard binary pointers have multiple bits switching simultaneously, they cannot safely cross asynchronous boundaries. They must be converted to Gray code (where only one bit flips at a time) before being synchronized via multi-stage flip-flops (Synchronizers) into the destination domain.
Flag Generation: The full flag is generated only in the Write Domain, and the empty flag is generated only in the Read Domain.
2. Example Waveform Analysis
The waveform details a sequence of operations:
Phase 1 (Basic Writes & Flag): We write data AAAA, BBBB, and CCCC consecutively. This triggers the write pointer to advance (0 -> 1 -> 2 -> 3). The faster write clock means these happen quickly.
Phase 2 (Pointer Crossover & Reads): The Gray code of wr_ptr begins crossing into the Read Domain. We then perform two consecutive reads (rd_en is high). Note the slower rd_clk period. Data AAAA is read, followed by BBBB.
Phase 3 (Concurrent Read/Write): Both wr_en and rd_en are active simultaneously, showing the FIFO managing bidirectional traffic on separate, uncorrelated clock domains. wr_ptr advances to 4, and rd_ptr advances to 2.
An asynchronous FIFO is used to transfer data safely between two completely independent (asynchronous) clock domains. To prevent metastability, this design converts binary pointers to Gray code before passing them across the clock domains via multi-stage synchronizers.
Asynchronous FIFO Block Diagram
An Asynchronous FIFO relies on cross-domain pointer synchronization. The write pointer is synchronized into the read clock domain to calculate the empty flag, and the read pointer is synchronized into the write clock domain to calculate the full flag.
`timescale 1ns / 1ps
module async_fifo #( parameter int DATA_WIDTH = 8, // Data bus width parameter int FIFO_DEPTH = 16 // FIFO Depth (Must be a power of 2) )( // Write Domain Interface input logic wr_clk, input logic wr_rst_n, input logic wr_en, input logic [DATA_WIDTH-1:0] wr_data, output logic full,
Gray Code Optimization: Passing standard binary pointers (011 $\rightarrow$ 100) across clock domains creates hazardous transitions where multiple bits switch at once. If sampled mid-cycle, the synchronizer will capture corrupt states. Gray code changes by only one bit at a time, neutralizing multi-bit synchronization hazards.
Asynchronous Flags: The flags are evaluated in their respective destination domains to prevent starvation or overrun.
full is generated purely on wr_clk using the synchronized rd_ptr.
empty is generated purely on rd_clk using the synchronized wr_ptr.
Pessimistic Flag Boundaries: Because it takes 2 clock cycles for pointers to cross over the synchronizer network, the flags are inherently “pessimistic” but completely safe. The FIFO might report full for a couple of cycles longer than it actually is, or empty for slightly longer, but it will never underflow or overflow data.
Here is a comprehensive self-checking SystemVerilog testbench to verify the asynchronous FIFO. It instantiates the FIFO with independent write and read clock domains, generates random data patterns, and uses a scoreboard-style array to verify that the data read matches the data written in a strict First-In, First-Out order.
`timescale 1ns / 1ps
module tb_async_fifo;
// --- Parameters ---
localparam int W = 8; // Data Width
localparam int D = 16; // FIFO Depth
// --- Write Domain Signals ---
logic wr_clk;
logic wr_rst_n;
logic wr_en;
logic [W-1:0] wr_data;
logic full;
// --- Read Domain Signals ---
logic rd_clk;
logic rd_rst_n;
logic rd_en;
logic [W-1:0] rd_data;
logic empty;
// --- Testbench Internal Verification Queue ---
// Acts as our behavioral reference model (Golden Model)
Asynchronous Clock Architecture: The simulation establishes an arbitrary $\text{100 MHz}$ write domain and a completely unaligned $\text{66.67 MHz}$ read domain to mimic hardware clock jitter and cross-domain phase variations.
Scoreboard Modeling: The verification environment uses a SystemVerilog unbounded dynamic queue (expected_queue [$]). When data is written to the physical DUT, it’s also pushed to the back of the queue. When data is verified on output, it’s checked against the front of the queue.
Boundary Validation: Test Cases 2 and 3 intentionally target the corner states (full and empty) to ensure pointer overflow prevents hardware faults or data overwrites.
Concurrent Assertions Simulation (fork-join): Test Case 4 triggers parallel execution, stressing the Gray code synchronizers by modifying pointers across both interfaces simultaneously.
To ensure that the Asynchronous FIFO functions reliably under all circumstances, we can embed SystemVerilog Assertions (SVA) directly inside the module or bind them externally.
However, looking closely at your requirement: “fifo full and fifo empty can never occur” is a critical rule to clarify. In a functional FIFO, reaching a full or empty state is completely normal and expected behavior when the data rates don’t match.
What must never happen are the catastrophic failures associated with those states:
Overflow: Writing data when the FIFO is already full (corrupting data).
Underflow: Reading data when the FIFO is already empty (reading garbage data).
Illegal State: The FIFO stating it is simultaneously full and empty (unless the FIFO depth is 0, which is physically impossible here).
Here is the SystemVerilog code containing the formal assertions to catch these exact violation conditions.
SystemVerilog Assertion Code Block
You can place this code block directly at the bottom of your async_fifo module (before endmodule) or inside your testbench. These can also be reused for the synchronous FIFO testing.
// ========================================================================= // SYSTEMVERILOG ASSERTIONS (SVA) FOR SAFETY AND INTEGRITY // =========================================================================
// 1. Mutex Check: FIFO cannot be Full and Empty at the same time assert_full_and_empty_mutex: assert property ( @(posedge wr_clk) disable iff (!wr_rst_n) !(full && empty) ) else $error(“[SVA ERROR] FIFO is simultaneously FULL and EMPTY! Hardware state is corrupted.”);
// 2. Overflow Check: If FIFO is full, a write enable (wr_en) must NOT occur assert_no_overflow: assert property ( @(posedge wr_clk) disable iff (!wr_rst_n) full |-> !wr_en ) else $error(“[SVA ERROR] FIFO Overflow Detected! Write asserted while FIFO is full.”);
// 3. Underflow Check: If FIFO is empty, a read enable (rd_en) must NOT occur assert_no_underflow: assert property ( @(posedge rd_clk) disable iff (!rd_rst_n) empty |-> !rd_en ) else $error(“[SVA ERROR] FIFO Underflow Detected! Read asserted while FIFO is empty.”);
disable iff (!wr_rst_n): This ensures that assertions are turned off while the system is resetting. Checking for overflow/underflow during initialization leads to false positives.
|-> (Implication Operator): This means “If the left side is true, the right side must be true at that exact same clock cycle.” * For example, full |-> !wr_en translates to: If full is active, then wr_en must be evaluated as 0.
Behavioral Verification Matrix
When you run your testbench with an EDA tool (like Questa, VCS, or Riviera-PRO) with assertions enabled, the simulator will continuously track these properties:
Evaluated Property
Clock Domain
Expected Status
If Violated…
!(full && empty)
wr_clk
Always True
Simulation flags a critical state error.
`full
-> !wr_en`
wr_clk
True
`empty
-> !rd_en`
rd_clk
True
IEEE floating point addition example
to be added
IEEE 754 floating point comparator example
/*
Do not change Module name */ // IEEE 754 binary32 : one bit sign, 8 bit exponent, 23 bit fraction bits // David Fong
module fp_compare ( input [31:0] float_a, // float A input [31:0] float_b, // float B output reg greater_than, // A > B output reg less_than, // A < B output
// compare signs if (sign_a < sign_b) begin // 0 means pos, 1 means neg greater_than = 1; less_than = 0; equal = 0; end else if (sign_a > sign_b) begin greater_than = 0; less_than = 1; equal = 0; end else begin // compare exponent assuming unsigned 0 to 255 if (exp_a > exp_b) begin greater_than = 1; less_than = 0; equal = 0; end else if (exp_a < exp_b) begin greater_than = 0; less_than = 1; equal = 0; end else begin // compare mantissa portion if (mant_a > mant_b) begin greater_than = 1; less_than = 0; equal = 0; end else if (mant_a < mant_b) begin greater_than = 0; less_than = 1; equal = 0; end else begin // last begin // Numbers are equal greater_than = 0; less_than = 0; equal = 1; end // matching last end end end
Rate Adapter or Upsizer Example (convert 32 bit to 256 bit using valid/ready handshake)
Below is the complete, synthesizable SystemVerilog code for a Data Rate Adapter (sometimes called a data width converter or funnel/upsizer).
This module accumulates eight consecutive 32-bit words from a master to form a single 256-bit word, which is then passed to a downstream slave using a standard Valid/Ready handshake mechanism.
// Output assignment mapping the packed internal array to the continuous 256-bit bus
assign m_axis_tdata = data_buffer;
assign m_axis_tvalid = out_valid_reg;
// --- Control and Data Accumulation Path ---
always_ff @(posedge clk) begin
if (!rst_n) begin
word_count <= '0;
out_valid_reg <= 1'b0;
data_buffer <= '0;
end else begin
// 1. Downstream Handshake Management
// If downstream slave accepts the 256-bit packet, clear the valid flag
if (m_axis_tvalid && m_axis_tready) begin
out_valid_reg <= 1'b0;
end
// 2. Upstream Data Accumulation
// Accept input when master provides valid data and we are ready
if (s_axis_tvalid && s_axis_tready) begin
// Store the incoming 32-bit word into the corresponding slot
data_buffer[word_count] <= s_axis_tdata;
// Increment word counter. It naturally wraps around from 7 to 0 (3-bit counter)
word_count <= word_count + 1'b1;
// When reaching the 8th word (index 3'b111), register the output as valid
if (word_count == 3'b111) begin
out_valid_reg <= 1'b1;
end
end
end
end
endmodule
Architectural & Handshake Design Highlights
Backpressure Support: The s_axis_tready signal ensures that the upstream master pauses if the downstream network stalls. If the 256-bit output buffer is full (out_valid_reg == 1), it will withhold s_axis_tready until m_axis_tready goes high to empty the register.
Array Index Mapping: The buffer uses a packed multi-dimensional array logic [7:0][31:0] data_buffer. This simplifies indexing (data_buffer[word_count]), which synthesis tools automatically map to standard multiplexer logic.
Zero-Bubble Cycling: When the 8th word arrives simultaneously with a downstream read request (m_axis_tready), the module immediately transitions to accumulating the next packet without inserting dead clock cycles.
Example Verification Testbench
This testbench feeds sequential data to verify accumulation boundaries and introduces random downstream ready drops to test handshaking robustness.
As a television ad once promoted, we live in a modern day society where we want to :
Share this. Share that.
How can we do that?
The current set of millions of smartphones (iPhones) can easily share voice and text conversations and photos.
But what’s next to do.
We build some intelligence into devices to allow remote monitoring and other sensing applications so that we can share even more information related to devices like washing machines, refrigerators, lights, sprinklers, home security, and even smart watches to monitor health conditions in real-time.
This growing network of interconnected devices will expand the internet bandwidth requirements.
This new market for intelligent internet of things (IoT) is generally segmented into these categories with the top two already existing and the remainder needing to be built or already in design and production. These are ordered in somewhat reducing complexity:
Servers / Routers
Smartphones / Tablets / Home PCs / Laptops
Wearable infotainment
Wearable Fitness and Health
Smart Home
Smart Appliance
Safety and Security
Smart City / Metering
Commerce
Reference Synopsys analysis of IP components needed to build these IoT devices
The high-end Internet of Things are occupied by the Smartphones/Tablets/Home PCs/Laptop and an example SOC diagram would like below with CPU, display, LDDR and sensors. These usually use the latest technology process nodes like 10nm FinFET to make the billion transistor devices economical and probably cost between $20 to $100 per device.
LOW-END SOC FOR IOT
The low-end SOC for IoT has significantly reduced IP but maintains bare functional and communication capabilities such as as a CPU, bluetooth and sensor. These would probably go into smart metering devices and use 28nm process node technology and cost around $5 to $20.
BOTTOM LOW-END SOC FOR IOT
The bottom SOC for IoT can be a bare minimum of CPU, some local storage RAM and sensor. Cost should be within a $1 or less to be economically feasible and produced in the billions. It can probably use older process nodes like 90nm and cost less than $1 to manufacture. These could be added as tracking devices for very expensive items shipped across international borders.