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 <seanga2@gmail.com>
This commit is contained in:
Sean Anderson 2023-02-15 11:03:38 -05:00
parent 16b639aad2
commit d98e7b3adf
3 changed files with 108 additions and 0 deletions

View File

@ -113,6 +113,7 @@ endef
MODULES += axis_replay_buffer MODULES += axis_replay_buffer
MODULES += descramble MODULES += descramble
MODULES += led_blinker
MODULES += mdio MODULES += mdio
MODULES += mdio_io MODULES += mdio_io
MODULES += mdio_regs MODULES += mdio_regs

64
rtl/led_blinker.v Normal file
View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: AGPL-3.0-Only
/*
* Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
*
* 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

43
tb/led_blinker.py Normal file
View File

@ -0,0 +1,43 @@
# SPDX-License-Identifier: AGPL-3.0-Only
# Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
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