io: Add LED shift register driver module

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-02-25 15:44:57 -08:00
parent 6e90f4f0a0
commit 5a8ac23922

155
rtl/io/taxi_led_sreg.sv Normal file
View File

@@ -0,0 +1,155 @@
// SPDX-License-Identifier: CERN-OHL-S-2.0
/*
Copyright (c) 2020-2025 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* LED shift register driver
*/
module taxi_led_sreg #(
// number of LEDs
parameter COUNT = 8,
// invert output
parameter logic INVERT = 1'b0,
// reverse order
parameter logic REVERSE = 1'b0,
// interleave A and B inputs, otherwise only use A
parameter logic INTERLEAVE = 1'b0,
// clock prescale
parameter PRESCALE = 31
)
(
input wire logic clk,
input wire logic rst,
input wire logic [COUNT-1:0] led_a,
input wire logic [COUNT-1:0] led_b,
output wire logic sreg_d,
output wire logic sreg_ld,
output wire logic sreg_clk
);
localparam COUNT_INT = INTERLEAVE ? COUNT*2 : COUNT;
localparam CL_COUNT = $clog2(COUNT_INT);
localparam CL_PRESCALE = $clog2(PRESCALE+1);
logic [CL_COUNT+1-1:0] count_reg = 0;
logic [CL_PRESCALE-1:0] prescale_count_reg = 0;
logic enable_reg = 1'b0;
logic update_reg = 1'b1;
logic cycle_reg = 1'b0;
logic [COUNT_INT-1:0] led_sync_reg_1 = 0;
logic [COUNT_INT-1:0] led_sync_reg_2 = 0;
logic [COUNT_INT-1:0] led_reg = 0;
logic sreg_d_reg = 1'b0;
logic sreg_ld_reg = 1'b0;
logic sreg_clk_reg = 1'b0;
assign sreg_d = INVERT ? !sreg_d_reg : sreg_d_reg;
assign sreg_ld = sreg_ld_reg;
assign sreg_clk = sreg_clk_reg;
wire [COUNT_INT-1:0] led_in;
wire [COUNT_INT-1:0] led_sync;
if (INTERLEAVE) begin
for (genvar i = 0; i < COUNT; i = i + 1) begin
assign led_in[i*2 +: 2] = {led_b[i], led_a[i]};
end
end else begin
assign led_in = led_a;
end
taxi_sync_signal #(
.WIDTH(COUNT_INT),
.N(2)
)
sync_inst (
.clk(clk),
.in(led_in),
.out(led_sync)
);
always @(posedge clk) begin
enable_reg <= 1'b0;
if (prescale_count_reg != 0) begin
prescale_count_reg <= prescale_count_reg - 1;
end else begin
enable_reg <= 1'b1;
prescale_count_reg <= PRESCALE;
end
if (enable_reg) begin
if (cycle_reg) begin
cycle_reg <= 1'b0;
sreg_clk_reg <= 1'b1;
end else if (count_reg != 0) begin
sreg_clk_reg <= 1'b0;
sreg_ld_reg <= 1'b0;
if (count_reg < COUNT_INT) begin
count_reg <= count_reg + 1;
cycle_reg <= 1'b1;
if (REVERSE) begin
sreg_d_reg <= led_reg[CL_COUNT'(COUNT_INT-1-count_reg)];
end else begin
sreg_d_reg <= led_reg[CL_COUNT'(count_reg)];
end
end else begin
count_reg <= 0;
cycle_reg <= 1'b0;
sreg_d_reg <= 1'b0;
sreg_ld_reg <= 1'b1;
end
end else begin
sreg_clk_reg <= 1'b0;
sreg_ld_reg <= 1'b0;
if (update_reg) begin
update_reg <= 1'b0;
count_reg <= 1;
cycle_reg <= 1'b1;
if (REVERSE) begin
sreg_d_reg <= led_reg[COUNT_INT-1];
end else begin
sreg_d_reg <= led_reg[0];
end
end
end
end
if (led_sync != led_reg) begin
led_reg <= led_sync;
update_reg <= 1'b1;
end
if (rst) begin
count_reg <= 0;
prescale_count_reg <= 0;
enable_reg <= 1'b0;
update_reg <= 1'b1;
cycle_reg <= 1'b0;
led_reg <= 0;
sreg_d_reg <= 1'b0;
sreg_ld_reg <= 1'b0;
sreg_clk_reg <= 1'b0;
end
end
endmodule
`resetall