From d98e7b3adf12bde54f5676fa8e748a0bd9dc2c06 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Wed, 15 Feb 2023 11:03:38 -0500 Subject: [PATCH] Add LED blinker This module will make it easier to observe internal signals which would otherwise be too short to see, or would trigger too fast to distinguish. Continuous triggered will cause blinking, so signals which are expected to be high for a while (e.g. level-based and not edge-based) should not use this module. Signed-off-by: Sean Anderson --- Makefile | 1 + rtl/led_blinker.v | 64 +++++++++++++++++++++++++++++++++++++++++++++++ tb/led_blinker.py | 43 +++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 rtl/led_blinker.v create mode 100644 tb/led_blinker.py diff --git a/Makefile b/Makefile index 6538175..f190fef 100644 --- a/Makefile +++ b/Makefile @@ -113,6 +113,7 @@ endef MODULES += axis_replay_buffer MODULES += descramble +MODULES += led_blinker MODULES += mdio MODULES += mdio_io MODULES += mdio_regs diff --git a/rtl/led_blinker.v b/rtl/led_blinker.v new file mode 100644 index 0000000..59e07e5 --- /dev/null +++ b/rtl/led_blinker.v @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: AGPL-3.0-Only +/* + * Copyright (C) 2023 Sean Anderson + * + * This is an LED blinker designed to make it easier to monitor internal + * signals with LEDs. The blinker is active for ~16 and inactive for ~16 ms. + * When triggered, the corresponding output will go high the next time + * the blinker becomes active. This results in blinking at 30 Hz if + * continuously triggered. All outputs blink at the same time. + */ + +`include "common.vh" + +module led_blinker ( + input clk, + input [LEDS - 1:0] triggers, + output reg [LEDS - 1:0] out, + + input test_mode +); + + parameter LEDS = 2; + + localparam TIMER_RESET = 21'h1ffffe; + /* 16 cycles before the end */ + localparam TEST_TIMER_RESET = 21'h0ccccf; + + reg active, active_next; + reg [LEDS - 1:0] out_next, triggered, triggered_next; + reg [20:0] lfsr, lfsr_next; + + initial begin + active = 0; + triggered = {LEDS{1'b0}}; + out = {LEDS{1'b0}}; + lfsr = TEST_TIMER_RESET; + end + + always @(*) begin + active_next = active; + triggered_next = triggered | triggers; + out_next = out; + lfsr_next = { lfsr[19:0], lfsr[20] ^ lfsr[18] }; + if (&lfsr) begin + if (active) begin + active_next = 0; + triggered_next = triggered_next & ~out; + out_next = {LEDS{1'b0}}; + end else begin + active_next = 1; + out_next = triggered; + end + lfsr_next = test_mode ? 21'hCCCCF : 21'h1FFFFE; + end + end + + always @(posedge clk) begin + active <= active_next; + triggered <= triggered_next; + out <= out_next; + lfsr <= lfsr_next; + end + +endmodule diff --git a/tb/led_blinker.py b/tb/led_blinker.py new file mode 100644 index 0000000..60e21bd --- /dev/null +++ b/tb/led_blinker.py @@ -0,0 +1,43 @@ +# 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 ClockCycles, FallingEdge, Timer + +from .util import BIT + +@cocotb.test(timeout_time=1, timeout_unit='us') +async def test_elastic(led): + led.clk.value = BinaryValue('Z') + led.triggers.value = 0 + led.test_mode.value = 1 + + await Timer(1) + await cocotb.start(Clock(led.clk, 2, units='ns').start()) + + await FallingEdge(led.clk) + assert not led.out.value + + led.triggers.value = 1 + await FallingEdge(led.clk) + assert not led.out.value + + led.triggers.value = 0 + while not led.out.value: + await FallingEdge(led.clk) + assert led.out.value == 1 + + led.triggers.value = 1 + await FallingEdge(led.clk) + + led.triggers.value = 0 + await ClockCycles(led.clk, 16, False) + led.triggers.value = 2 + + await FallingEdge(led.clk) + assert not led.out.value + while not led.out.value: + await FallingEdge(led.clk) + assert led.out.value == 2