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:
parent
16b639aad2
commit
d98e7b3adf
1
Makefile
1
Makefile
|
@ -113,6 +113,7 @@ endef
|
|||
|
||||
MODULES += axis_replay_buffer
|
||||
MODULES += descramble
|
||||
MODULES += led_blinker
|
||||
MODULES += mdio
|
||||
MODULES += mdio_io
|
||||
MODULES += mdio_regs
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue