From 924079cabd8035fac7398dc3f2e4107510515e3a Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Sun, 5 Mar 2023 16:42:16 -0500 Subject: [PATCH] Add reset synchronizer Add a reset synchronizer to ensure synchronous reset release. There is also a glitch filter to reject spurious resets. It will reject pulses shorter than 5 ns (or around 1.25 ns per LUT). Signed-off-by: Sean Anderson --- Makefile | 1 + README.adoc | 6 ++++++ rtl/reset_sync.v | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ tb/reset_sync.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 rtl/reset_sync.v create mode 100644 tb/reset_sync.py diff --git a/Makefile b/Makefile index e4d9722..b13f3b6 100644 --- a/Makefile +++ b/Makefile @@ -146,6 +146,7 @@ MODULES += pcs_tx MODULES += phy_core MODULES += pmd_dp83223 MODULES += pmd_dp83223_rx +MODULES += reset_sync MODULES += scramble MODULES += uart_tx MODULES += uart_rx diff --git a/README.adoc b/README.adoc index 946338f..a8fdad6 100644 --- a/README.adoc +++ b/README.adoc @@ -204,6 +204,12 @@ entire rest of the data path up to the PCS (when we can finally align the data) must handle these edge cases. However, it avoids the internal, nebulously-specified, and limited-in-number iCE40 PLLs. +=== `reset_sync` + +This module synchronizes external reset signals (asynchronous assert and +release) into the local clock domain (asynchronous assert, desynchronous +release). A glitch filter suppresses spurious resets. + === `scramble` This module implements a scrambler as described in ANSI X3.264-1995 section diff --git a/rtl/reset_sync.v b/rtl/reset_sync.v new file mode 100644 index 0000000..bc236a7 --- /dev/null +++ b/rtl/reset_sync.v @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: AGPL-3.0-Only +/* + * Copyright (C) 2023 Sean Anderson + */ + +`include "common.vh" + +module reset_sync ( + input clk, + input rst_in, + output reg rst_out +); + + wire rst; + reg rst_last; + initial rst_last = 1; + initial rst_out = 1; + +`ifdef SYNTHESIS + /* Filter out glitches */ + + wire [3:0] rst_delay; + assign rst_delay[0] = rst_in; + assign rst = &rst_delay; + + genvar i; + generate for (i = 0; i < 3; i = i + 1) begin + (* keep *) + SB_LUT4 #( + .LUT_INIT(16'hff00) + ) filter ( + .I3(rst_delay[i]), + .O(rst_delay[i + 1]) + ); + end endgenerate +`else + assign rst = rst_in; +`endif + + always @(posedge clk, posedge rst) begin + if (rst) begin + rst_last <= 1; + rst_out <= 1; + end else begin + rst_last <= rst; + rst_out <= rst_last; + end + end + +endmodule diff --git a/tb/reset_sync.py b/tb/reset_sync.py new file mode 100644 index 0000000..94e53bb --- /dev/null +++ b/tb/reset_sync.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: AGPL-3.0-Only +# Copyright (C) 2023 Sean Anderson + +import cocotb +from cocotb.binary import BinaryValue +from cocotb.clock import Clock +from cocotb.triggers import ReadOnly, RisingEdge, Timer + +@cocotb.test(timeout_time=100, timeout_unit='us') +async def test_bridge(sync): + sync.clk.value = BinaryValue('Z') + sync.rst_in.value = 0 + + await Timer(1) + assert sync.rst_out.value + await cocotb.start(Clock(sync.clk, 8, units='ns').start()) + + await RisingEdge(sync.clk) + assert sync.rst_out.value + + await RisingEdge(sync.clk) + assert sync.rst_out.value + + await RisingEdge(sync.clk) + assert not sync.rst_out.value + + await Timer(1) + assert not sync.rst_out.value + sync.rst_in.value = 1 + + await ReadOnly() + assert sync.rst_out.value + + await Timer(1) + sync.rst_in.value = 0 + assert sync.rst_out.value + + await RisingEdge(sync.clk) + assert sync.rst_out.value + + await RisingEdge(sync.clk) + assert sync.rst_out.value + + await RisingEdge(sync.clk) + assert not sync.rst_out.value