Compare commits
7 commits
ba1aa9181e
...
79fce1afc1
Author | SHA1 | Date | |
---|---|---|---|
79fce1afc1 | |||
9121ccfdbe | |||
d5b0ee2cfa | |||
2ec250e79d | |||
57e6daedcc | |||
94ff182aec | |||
01fe200d92 |
5 changed files with 290 additions and 122 deletions
15
README.md
Normal file
15
README.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# WS2812 driver gateware for blinkenwall v3
|
||||||
|
|
||||||
|
At 100 FPS, a single strand of WS2812 can only be just over 300 LEDs long:
|
||||||
|
|
||||||
|
```
|
||||||
|
MAX_LENGTH = (1 / FPS - RESET_TIME) / BIT_TIME / BITS_PER_PIXEL
|
||||||
|
318 = (1 / 100 - 50e-6) / 1.3e-6 / 24
|
||||||
|
```
|
||||||
|
|
||||||
|
Because blinkenwall v3 has `64 * 96 = 6144` pixels, driving it at 100 FPS requires at least
|
||||||
|
`6144 / 318 = 20` parallel drivers. For simplicity, the wall is devided into 24 strands of
|
||||||
|
256 LEDs. Unfortunately, most microcontrollers have at most a couple WS2812-capable interfaces
|
||||||
|
(typically SPI), so this would require coordinating several microcontrollers in parallel. A
|
||||||
|
much more integrated solution is to instantiate as many WS2812 drivers as desired in an FPGA,
|
||||||
|
then point the entire video firehose at the FPGA.
|
|
@ -222,7 +222,7 @@ set_property IOSTANDARD LVCMOS33 [get_ports {ck_dig_h[13]}]
|
||||||
set_property IOSTANDARD LVCMOS33 [get_ports {ck_dig_h[14]}]
|
set_property IOSTANDARD LVCMOS33 [get_ports {ck_dig_h[14]}]
|
||||||
set_property IOSTANDARD LVCMOS33 [get_ports {ck_dig_h[15]}]
|
set_property IOSTANDARD LVCMOS33 [get_ports {ck_dig_h[15]}]
|
||||||
|
|
||||||
create_clock -period 12.5 [get_nets clk_sys]
|
create_clock -period 12.5 [get_nets sys_clk]
|
||||||
|
|
||||||
create_clock -period 40.0 [get_nets liteeth_inst.eth_rx_clk]
|
create_clock -period 40.0 [get_nets liteeth_inst.eth_rx_clk]
|
||||||
create_clock -period 40.0 [get_nets liteeth_inst.eth_tx_clk]
|
create_clock -period 40.0 [get_nets liteeth_inst.eth_tx_clk]
|
||||||
|
|
142
gen_liteeth.py
142
gen_liteeth.py
|
@ -28,6 +28,7 @@ from liteeth.common import *
|
||||||
from liteeth import phy as liteeth_phys
|
from liteeth import phy as liteeth_phys
|
||||||
from liteeth.mac import LiteEthMAC
|
from liteeth.mac import LiteEthMAC
|
||||||
from liteeth.core import LiteEthUDPIPCore
|
from liteeth.core import LiteEthUDPIPCore
|
||||||
|
from liteeth.core.udp import LiteEthUDP
|
||||||
|
|
||||||
# IOs ----------------------------------------------------------------------------------------------
|
# IOs ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -40,12 +41,8 @@ _io = [
|
||||||
("sys_reset", 1, Pins(1)),
|
("sys_reset", 1, Pins(1)),
|
||||||
|
|
||||||
# IP/MAC Address.
|
# IP/MAC Address.
|
||||||
("mac_address", 0, Pins(48)),
|
|
||||||
("ip_address", 0, Pins(32)),
|
("ip_address", 0, Pins(32)),
|
||||||
|
|
||||||
# Interrupt
|
|
||||||
("interrupt", 0, Pins(1)),
|
|
||||||
|
|
||||||
# MII PHY Pads
|
# MII PHY Pads
|
||||||
("mii_eth_clocks", 0,
|
("mii_eth_clocks", 0,
|
||||||
Subsignal("tx", Pins(1)),
|
Subsignal("tx", Pins(1)),
|
||||||
|
@ -63,61 +60,10 @@ _io = [
|
||||||
Subsignal("col", Pins(1)),
|
Subsignal("col", Pins(1)),
|
||||||
Subsignal("crs", Pins(1))
|
Subsignal("crs", Pins(1))
|
||||||
),
|
),
|
||||||
|
|
||||||
# RMII PHY Pads
|
|
||||||
("rmii_eth_clocks", 0,
|
|
||||||
Subsignal("ref_clk", Pins(1))
|
|
||||||
),
|
|
||||||
("rmii_eth", 0,
|
|
||||||
Subsignal("rst_n", Pins(1)),
|
|
||||||
Subsignal("rx_data", Pins(2)),
|
|
||||||
Subsignal("crs_dv", Pins(1)),
|
|
||||||
Subsignal("tx_en", Pins(1)),
|
|
||||||
Subsignal("tx_data", Pins(2)),
|
|
||||||
Subsignal("mdc", Pins(1)),
|
|
||||||
Subsignal("mdio", Pins(1)),
|
|
||||||
),
|
|
||||||
|
|
||||||
# GMII PHY Pads
|
|
||||||
("gmii_eth_clocks", 0,
|
|
||||||
Subsignal("tx", Pins(1)),
|
|
||||||
Subsignal("gtx", Pins(1)),
|
|
||||||
Subsignal("rx", Pins(1))
|
|
||||||
),
|
|
||||||
("gmii_eth", 0,
|
|
||||||
Subsignal("rst_n", Pins(1)),
|
|
||||||
Subsignal("int_n", Pins(1)),
|
|
||||||
Subsignal("mdio", Pins(1)),
|
|
||||||
Subsignal("mdc", Pins(1)),
|
|
||||||
Subsignal("rx_dv", Pins(1)),
|
|
||||||
Subsignal("rx_er", Pins(1)),
|
|
||||||
Subsignal("rx_data", Pins(8)),
|
|
||||||
Subsignal("tx_en", Pins(1)),
|
|
||||||
Subsignal("tx_er", Pins(1)),
|
|
||||||
Subsignal("tx_data", Pins(8)),
|
|
||||||
Subsignal("col", Pins(1)),
|
|
||||||
Subsignal("crs", Pins(1))
|
|
||||||
),
|
|
||||||
|
|
||||||
# RGMII PHY Pads
|
|
||||||
("rgmii_eth_clocks", 0,
|
|
||||||
Subsignal("tx", Pins(1)),
|
|
||||||
Subsignal("rx", Pins(1))
|
|
||||||
),
|
|
||||||
("rgmii_eth", 0,
|
|
||||||
Subsignal("rst_n", Pins(1)),
|
|
||||||
Subsignal("int_n", Pins(1)),
|
|
||||||
Subsignal("mdio", Pins(1)),
|
|
||||||
Subsignal("mdc", Pins(1)),
|
|
||||||
Subsignal("rx_ctl", Pins(1)),
|
|
||||||
Subsignal("rx_data", Pins(4)),
|
|
||||||
Subsignal("tx_ctl", Pins(1)),
|
|
||||||
Subsignal("tx_data", Pins(4))
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_udp_port_ios(name, data_width):
|
def get_udp_streamer_port_ios(name, data_width):
|
||||||
return [
|
return [
|
||||||
(f"{name}", 0,
|
(f"{name}", 0,
|
||||||
Subsignal("ip_address", Pins(32)),
|
Subsignal("ip_address", Pins(32)),
|
||||||
|
@ -137,6 +83,36 @@ def get_udp_port_ios(name, data_width):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_udp_port_ios(name, data_width):
|
||||||
|
return [
|
||||||
|
(f"{name}", 0,
|
||||||
|
Subsignal("bind_port", Pins(16)),
|
||||||
|
|
||||||
|
# Sink.
|
||||||
|
Subsignal("sink_dst_ip_address", Pins(32)),
|
||||||
|
Subsignal("sink_dst_port", Pins(16)),
|
||||||
|
|
||||||
|
Subsignal("sink_length", Pins(16)),
|
||||||
|
Subsignal("sink_valid", Pins(1)),
|
||||||
|
Subsignal("sink_last", Pins(1)),
|
||||||
|
Subsignal("sink_last_be", Pins(data_width // 8)),
|
||||||
|
Subsignal("sink_ready", Pins(1)),
|
||||||
|
Subsignal("sink_data", Pins(data_width)),
|
||||||
|
|
||||||
|
# Source.
|
||||||
|
Subsignal("source_src_ip_address", Pins(32)),
|
||||||
|
Subsignal("source_src_port", Pins(16)),
|
||||||
|
|
||||||
|
Subsignal("source_length", Pins(16)),
|
||||||
|
Subsignal("source_valid", Pins(1)),
|
||||||
|
Subsignal("source_last", Pins(1)),
|
||||||
|
Subsignal("source_last_be", Pins(data_width // 8)),
|
||||||
|
Subsignal("source_ready", Pins(1)),
|
||||||
|
Subsignal("source_data", Pins(data_width)),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class PHYCore(SoCMini):
|
class PHYCore(SoCMini):
|
||||||
def __init__(self, platform):
|
def __init__(self, platform):
|
||||||
super().__init__(platform, clk_freq=CLK_FREQ)
|
super().__init__(platform, clk_freq=CLK_FREQ)
|
||||||
|
@ -178,6 +154,16 @@ class PHYCore(SoCMini):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UDPPort(Module, AutoCSR):
|
||||||
|
def __init__(self, udp_core: LiteEthUDP, bind_port, data_width=8, cd="sys"):
|
||||||
|
udp_port = udp_core.crossbar.get_port(bind_port, dw=data_width, cd=cd)
|
||||||
|
self.sink = udp_port.sink
|
||||||
|
self.comb += [
|
||||||
|
self.sink.src_port.eq(bind_port),
|
||||||
|
]
|
||||||
|
self.source = udp_port.source
|
||||||
|
|
||||||
|
|
||||||
class UDPCore(PHYCore):
|
class UDPCore(PHYCore):
|
||||||
def __init__(self, platform):
|
def __init__(self, platform):
|
||||||
from liteeth.frontend.stream import LiteEthUDPStreamer
|
from liteeth.frontend.stream import LiteEthUDPStreamer
|
||||||
|
@ -199,7 +185,7 @@ class UDPCore(PHYCore):
|
||||||
# DHCP port
|
# DHCP port
|
||||||
data_width = 32
|
data_width = 32
|
||||||
|
|
||||||
platform.add_extension(get_udp_port_ios(
|
platform.add_extension(get_udp_streamer_port_ios(
|
||||||
"dhcp",
|
"dhcp",
|
||||||
data_width=data_width,
|
data_width=data_width,
|
||||||
))
|
))
|
||||||
|
@ -229,6 +215,46 @@ class UDPCore(PHYCore):
|
||||||
dhcp_ios.source_data.eq(dhcp_streamer.source.data),
|
dhcp_ios.source_data.eq(dhcp_streamer.source.data),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Pixel port
|
||||||
|
data_width = 32
|
||||||
|
|
||||||
|
platform.add_extension(get_udp_port_ios(
|
||||||
|
"pixel",
|
||||||
|
data_width=data_width,
|
||||||
|
))
|
||||||
|
pixel_ios = platform.request("pixel")
|
||||||
|
|
||||||
|
pixel_port = UDPPort(
|
||||||
|
self.core.udp,
|
||||||
|
bind_port=pixel_ios.bind_port,
|
||||||
|
data_width=data_width,
|
||||||
|
)
|
||||||
|
self.submodules += pixel_port
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# Connect UDP Sink IOs to UDPPort
|
||||||
|
pixel_port.sink.ip_address.eq(pixel_ios.sink_dst_ip_address),
|
||||||
|
pixel_port.sink.dst_port.eq(pixel_ios.sink_dst_port),
|
||||||
|
|
||||||
|
pixel_port.sink.length.eq(pixel_ios.sink_length),
|
||||||
|
pixel_port.sink.valid.eq(pixel_ios.sink_valid),
|
||||||
|
pixel_port.sink.last.eq(pixel_ios.sink_last),
|
||||||
|
pixel_port.sink.last_be.eq(pixel_ios.sink_last_be),
|
||||||
|
pixel_ios.sink_ready.eq(pixel_port.sink.ready),
|
||||||
|
pixel_port.sink.data.eq(pixel_ios.sink_data),
|
||||||
|
|
||||||
|
# Connect UDPPort to UDP Source IOs.
|
||||||
|
pixel_ios.source_src_ip_address.eq(pixel_port.source.ip_address),
|
||||||
|
pixel_ios.source_src_port.eq(pixel_port.source.src_port),
|
||||||
|
|
||||||
|
pixel_ios.source_length.eq(pixel_port.source.length),
|
||||||
|
pixel_ios.source_valid.eq(pixel_port.source.valid),
|
||||||
|
pixel_ios.source_last.eq(pixel_port.source.last),
|
||||||
|
pixel_ios.source_last_be.eq(pixel_port.source.last_be),
|
||||||
|
pixel_port.source.ready.eq(pixel_ios.source_ready),
|
||||||
|
pixel_ios.source_data.eq(pixel_port.source.data),
|
||||||
|
]
|
||||||
|
|
||||||
# Build --------------------------------------------------------------------------------------------
|
# Build --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -244,7 +270,7 @@ def main():
|
||||||
platform = XilinxPlatform(
|
platform = XilinxPlatform(
|
||||||
"xc7a35ticsg324-1L",
|
"xc7a35ticsg324-1L",
|
||||||
io=[],
|
io=[],
|
||||||
toolchain="symbiflow"
|
toolchain="yosys+nextpnr"
|
||||||
)
|
)
|
||||||
platform.add_extension(_io)
|
platform.add_extension(_io)
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ entity arty_a7 is
|
||||||
-- Pmod connectors - A+D standard, B+C high-speed.
|
-- Pmod connectors - A+D standard, B+C high-speed.
|
||||||
-- Defined as inputs by default for safety, change
|
-- Defined as inputs by default for safety, change
|
||||||
-- when necessary
|
-- when necessary
|
||||||
pmod_a : in std_logic_vector(7 downto 0);
|
pmod_a : out std_logic_vector(7 downto 0);
|
||||||
pmod_b : in std_logic_vector(7 downto 0);
|
pmod_b : out std_logic_vector(7 downto 0);
|
||||||
pmod_c : in std_logic_vector(7 downto 0);
|
pmod_c : in std_logic_vector(7 downto 0);
|
||||||
pmod_d : out std_logic_vector(7 downto 0);
|
pmod_d : out std_logic_vector(7 downto 0);
|
||||||
|
|
||||||
|
@ -46,13 +46,14 @@ entity arty_a7 is
|
||||||
end arty_a7;
|
end arty_a7;
|
||||||
|
|
||||||
architecture a of arty_a7 is
|
architecture a of arty_a7 is
|
||||||
constant NUM_DRIVERS: positive := 16;
|
constant NUM_STRANDS: positive := 24;
|
||||||
signal drivers: std_logic_vector(NUM_DRIVERS-1 downto 0);
|
signal drivers: std_logic_vector(NUM_STRANDS-1 downto 0);
|
||||||
|
|
||||||
component liteeth_core is
|
component liteeth_core is
|
||||||
port (
|
port (
|
||||||
sys_clock : in std_logic;
|
sys_clock : in std_logic;
|
||||||
sys_reset : in std_logic;
|
sys_reset : in std_logic;
|
||||||
|
|
||||||
mii_eth_clocks_tx : in std_logic;
|
mii_eth_clocks_tx : in std_logic;
|
||||||
mii_eth_clocks_rx : in std_logic;
|
mii_eth_clocks_rx : in std_logic;
|
||||||
mii_eth_rst_n : out std_logic;
|
mii_eth_rst_n : out std_logic;
|
||||||
|
@ -65,20 +66,65 @@ architecture a of arty_a7 is
|
||||||
mii_eth_tx_data : out std_logic_vector(3 downto 0);
|
mii_eth_tx_data : out std_logic_vector(3 downto 0);
|
||||||
mii_eth_col : in std_logic;
|
mii_eth_col : in std_logic;
|
||||||
mii_eth_crs : in std_logic;
|
mii_eth_crs : in std_logic;
|
||||||
|
|
||||||
ip_address : in std_logic_vector(31 downto 0);
|
ip_address : in std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
|
--== DHCP PORT ==--
|
||||||
dhcp_ip_address : in std_logic_vector(31 downto 0);
|
dhcp_ip_address : in std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
dhcp_sink_valid : in std_logic;
|
dhcp_sink_valid : in std_logic;
|
||||||
dhcp_sink_last : in std_logic;
|
dhcp_sink_last : in std_logic;
|
||||||
dhcp_sink_ready : out std_logic;
|
dhcp_sink_ready : out std_logic;
|
||||||
dhcp_sink_data : in std_logic_vector(31 downto 0);
|
dhcp_sink_data : in std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
dhcp_source_valid : out std_logic;
|
dhcp_source_valid : out std_logic;
|
||||||
dhcp_source_last : out std_logic;
|
dhcp_source_last : out std_logic;
|
||||||
dhcp_source_ready : in std_logic;
|
dhcp_source_ready : in std_logic;
|
||||||
dhcp_source_data : out std_logic_vector(31 downto 0)
|
dhcp_source_data : out std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
|
--== PIXEL DATA PORT ==--
|
||||||
|
pixel_bind_port : in std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
-- sink
|
||||||
|
pixel_sink_dst_ip_address : in std_logic_vector(31 downto 0);
|
||||||
|
pixel_sink_dst_port : in std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
pixel_sink_length : in std_logic_vector(15 downto 0);
|
||||||
|
pixel_sink_valid : in std_logic;
|
||||||
|
pixel_sink_last : in std_logic;
|
||||||
|
pixel_sink_last_be : in std_logic_vector(3 downto 0);
|
||||||
|
pixel_sink_ready : out std_logic;
|
||||||
|
pixel_sink_data : in std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
|
-- source
|
||||||
|
pixel_source_src_ip_address : out std_logic_vector(31 downto 0);
|
||||||
|
pixel_source_src_port : out std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
pixel_source_length : out std_logic_vector(15 downto 0);
|
||||||
|
pixel_source_valid : out std_logic;
|
||||||
|
pixel_source_last : out std_logic;
|
||||||
|
pixel_source_last_be : out std_logic_vector(3 downto 0);
|
||||||
|
pixel_source_ready : in std_logic;
|
||||||
|
pixel_source_data : out std_logic_vector(31 downto 0)
|
||||||
);
|
);
|
||||||
end component;
|
end component;
|
||||||
|
|
||||||
signal dhcp_source_valid : std_logic;
|
signal pixel_sink_length : std_logic_vector(15 downto 0);
|
||||||
|
signal pixel_sink_valid : std_logic;
|
||||||
|
signal pixel_sink_last : std_logic;
|
||||||
|
signal pixel_sink_last_be : std_logic_vector(3 downto 0);
|
||||||
|
signal pixel_sink_ready : std_logic;
|
||||||
|
signal pixel_sink_data : std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
|
signal pixel_source_src_ip_address : std_logic_vector(31 downto 0);
|
||||||
|
signal pixel_source_src_port : std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
signal pixel_source_length : std_logic_vector(15 downto 0);
|
||||||
|
signal pixel_source_valid : std_logic;
|
||||||
|
signal pixel_source_last : std_logic;
|
||||||
|
signal pixel_source_last_be : std_logic_vector(3 downto 0);
|
||||||
|
signal pixel_source_ready : std_logic;
|
||||||
|
signal pixel_source_data : std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
component PLLE2_BASE
|
component PLLE2_BASE
|
||||||
generic (
|
generic (
|
||||||
|
@ -132,8 +178,8 @@ architecture a of arty_a7 is
|
||||||
|
|
||||||
signal pll_feedback : std_logic;
|
signal pll_feedback : std_logic;
|
||||||
signal pll_locked : std_logic;
|
signal pll_locked : std_logic;
|
||||||
signal unbuf_clk_sys : std_logic;
|
signal unbuf_sys_clk : std_logic;
|
||||||
signal clk_sys : std_logic;
|
signal sys_clk : std_logic;
|
||||||
|
|
||||||
component BUFG
|
component BUFG
|
||||||
port (
|
port (
|
||||||
|
@ -144,7 +190,7 @@ architecture a of arty_a7 is
|
||||||
|
|
||||||
signal sys_reset : std_logic;
|
signal sys_reset : std_logic;
|
||||||
begin
|
begin
|
||||||
--leds_simple <= (others => '0');
|
leds_simple <= (others => '0');
|
||||||
led0 <= (others => '0');
|
led0 <= (others => '0');
|
||||||
led1 <= (others => '0');
|
led1 <= (others => '0');
|
||||||
led2 <= (others => '0');
|
led2 <= (others => '0');
|
||||||
|
@ -155,10 +201,8 @@ begin
|
||||||
ck_dig_l <= (others => 'Z');
|
ck_dig_l <= (others => 'Z');
|
||||||
ck_dig_h <= (others => 'Z');
|
ck_dig_h <= (others => 'Z');
|
||||||
|
|
||||||
leds_simple <= drivers(3 downto 0);
|
|
||||||
|
|
||||||
liteeth_inst: liteeth_core port map (
|
liteeth_inst: liteeth_core port map (
|
||||||
sys_clock => clk_sys,
|
sys_clock => sys_clk,
|
||||||
sys_reset => sys_reset,
|
sys_reset => sys_reset,
|
||||||
|
|
||||||
mii_eth_clocks_tx => mii_tx_clk,
|
mii_eth_clocks_tx => mii_tx_clk,
|
||||||
|
@ -181,8 +225,32 @@ begin
|
||||||
dhcp_sink_last => '1',
|
dhcp_sink_last => '1',
|
||||||
dhcp_sink_data => x"cafebebe",
|
dhcp_sink_data => x"cafebebe",
|
||||||
|
|
||||||
dhcp_source_valid => dhcp_source_valid,
|
dhcp_source_ready => '1',
|
||||||
dhcp_source_ready => '1'
|
|
||||||
|
--== PIXEL DATA PORT ==--
|
||||||
|
pixel_bind_port => x"effd", -- port 61437 - "PIXEL"
|
||||||
|
|
||||||
|
-- sink
|
||||||
|
pixel_sink_dst_ip_address => x"0a141e29",
|
||||||
|
pixel_sink_dst_port => x"303a", -- port 12346
|
||||||
|
|
||||||
|
pixel_sink_length => pixel_sink_length,
|
||||||
|
pixel_sink_valid => pixel_sink_valid,
|
||||||
|
pixel_sink_last => pixel_sink_last,
|
||||||
|
pixel_sink_last_be => pixel_sink_last_be,
|
||||||
|
pixel_sink_ready => pixel_sink_ready,
|
||||||
|
pixel_sink_data => pixel_sink_data,
|
||||||
|
|
||||||
|
-- source
|
||||||
|
pixel_source_src_ip_address => pixel_source_src_ip_address,
|
||||||
|
pixel_source_src_port => pixel_source_src_port,
|
||||||
|
|
||||||
|
pixel_source_length => pixel_source_length,
|
||||||
|
pixel_source_valid => pixel_source_valid,
|
||||||
|
pixel_source_last => pixel_source_last,
|
||||||
|
pixel_source_last_be => pixel_source_last_be,
|
||||||
|
pixel_source_ready => pixel_source_ready,
|
||||||
|
pixel_source_data => pixel_source_data
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 800 MHz VCO
|
-- 800 MHz VCO
|
||||||
|
@ -212,61 +280,69 @@ begin
|
||||||
CLKFBIN => pll_feedback,
|
CLKFBIN => pll_feedback,
|
||||||
CLKFBOUT => pll_feedback,
|
CLKFBOUT => pll_feedback,
|
||||||
|
|
||||||
CLKOUT0 => unbuf_clk_sys,
|
CLKOUT0 => unbuf_sys_clk,
|
||||||
CLKOUT1 => mii_clk_25mhz
|
CLKOUT1 => mii_clk_25mhz
|
||||||
);
|
);
|
||||||
|
|
||||||
bufg_clk_sys: BUFG
|
bufg_sys_clk: BUFG
|
||||||
port map (
|
port map (
|
||||||
I => unbuf_clk_sys,
|
I => unbuf_sys_clk,
|
||||||
O => clk_sys
|
O => sys_clk
|
||||||
);
|
);
|
||||||
|
|
||||||
sys_reset <= not pll_locked or not n_reset;
|
sys_reset <= not pll_locked or not n_reset;
|
||||||
|
|
||||||
ws2812_inst: entity work.ws2812
|
pmod_a <= drivers(7 downto 0);
|
||||||
generic map (
|
pmod_b <= drivers(15 downto 8);
|
||||||
NUM_LEDS => 20,
|
pmod_d <= drivers(23 downto 16);
|
||||||
COLOR_ORDER => "GRB",
|
|
||||||
T_CLK => 12.5 ns,
|
|
||||||
|
|
||||||
T0H => 0.35 us,
|
sender: process(sys_clk)
|
||||||
T0L => 0.8 us,
|
constant COUNTER_MAX: natural := 80000000;
|
||||||
T1H => 0.7 us,
|
variable counter: natural range 0 to COUNTER_MAX;
|
||||||
T1L => 0.6 us,
|
|
||||||
|
|
||||||
T_RES => 80 us
|
constant NUM_WORDS: natural := 10;
|
||||||
)
|
variable words_sent: natural range 0 to NUM_WORDS;
|
||||||
port map (
|
begin
|
||||||
n_reset => not sys_reset,
|
if rising_edge(sys_clk) then
|
||||||
clk => clk_sys,
|
if counter = COUNTER_MAX then
|
||||||
|
pixel_sink_length <= std_logic_vector(to_unsigned(NUM_WORDS, 16));
|
||||||
|
pixel_sink_data <= std_logic_vector(to_unsigned(16#30# + words_sent, 32));
|
||||||
|
--pixel_sink_valid <= '1';
|
||||||
|
|
||||||
led_addr => open,
|
pixel_sink_last <= '1' when words_sent = NUM_WORDS-1 else '0';
|
||||||
|
|
||||||
led_red => x"ff",
|
if words_sent = NUM_WORDS then
|
||||||
led_green => x"00",
|
pixel_sink_valid <= '0';
|
||||||
led_blue => x"ff",
|
counter := 0;
|
||||||
|
words_sent := 0;
|
||||||
dout => pmod_d(3)
|
elsif pixel_sink_ready then
|
||||||
);
|
words_sent := words_sent + 1;
|
||||||
|
end if;
|
||||||
-- https://github.com/YosysHQ/yosys/issues/3360
|
else
|
||||||
pmod_d(0) <= '0';
|
counter := counter + 1;
|
||||||
pmod_d(1) <= '0';
|
end if;
|
||||||
pmod_d(2) <= '0';
|
end if;
|
||||||
pmod_d(4) <= '0';
|
end process;
|
||||||
pmod_d(5) <= '0';
|
pixel_sink_last_be <= (pixel_sink_last, others => '0');
|
||||||
pmod_d(6) <= '0';
|
|
||||||
pmod_d(7) <= '0';
|
|
||||||
|
|
||||||
splink: entity work.splink
|
splink: entity work.splink
|
||||||
generic map (
|
generic map (
|
||||||
NUM_DRIVERS => NUM_DRIVERS
|
NUM_STRANDS => NUM_STRANDS
|
||||||
)
|
)
|
||||||
port map (
|
port map (
|
||||||
clk => clk_sys,
|
clk => sys_clk,
|
||||||
reset => sys_reset,
|
reset => sys_reset,
|
||||||
|
|
||||||
|
udp_src_ip_address => pixel_source_src_ip_address,
|
||||||
|
udp_src_port => pixel_source_src_port,
|
||||||
|
|
||||||
|
udp_length => pixel_source_length,
|
||||||
|
udp_valid => pixel_source_valid,
|
||||||
|
udp_last => pixel_source_last,
|
||||||
|
udp_last_be => pixel_source_last_be,
|
||||||
|
udp_ready => pixel_source_ready,
|
||||||
|
udp_data => pixel_source_data,
|
||||||
|
|
||||||
drivers => drivers
|
drivers => drivers
|
||||||
);
|
);
|
||||||
end architecture;
|
end architecture;
|
||||||
|
|
|
@ -4,33 +4,84 @@ use ieee.numeric_std.all;
|
||||||
|
|
||||||
entity splink is
|
entity splink is
|
||||||
generic (
|
generic (
|
||||||
NUM_DRIVERS : positive := 16;
|
NUM_STRANDS : positive;
|
||||||
ROWS : positive := 100;
|
MAX_STRAND_LEN : positive := 256
|
||||||
COLS : positive := 100
|
|
||||||
);
|
);
|
||||||
port (
|
port (
|
||||||
clk : in std_logic;
|
clk : in std_logic;
|
||||||
reset : in std_logic;
|
reset : in std_logic;
|
||||||
|
|
||||||
drivers : out std_logic_vector(NUM_DRIVERS-1 downto 0)
|
udp_src_ip_address : in std_logic_vector(31 downto 0);
|
||||||
|
udp_src_port : in std_logic_vector(15 downto 0);
|
||||||
|
|
||||||
|
udp_length : in std_logic_vector(15 downto 0);
|
||||||
|
udp_valid : in std_logic;
|
||||||
|
udp_last : in std_logic;
|
||||||
|
udp_last_be : in std_logic_vector(3 downto 0);
|
||||||
|
udp_ready : out std_logic;
|
||||||
|
udp_data : in std_logic_vector(31 downto 0);
|
||||||
|
|
||||||
|
drivers : out std_logic_vector(NUM_STRANDS-1 downto 0)
|
||||||
);
|
);
|
||||||
end entity;
|
end entity;
|
||||||
|
|
||||||
architecture a of splink is
|
architecture a of splink is
|
||||||
signal count2: unsigned(3 downto 0);
|
signal driver_out : std_logic;
|
||||||
signal count: natural range 0 to 10000000;
|
|
||||||
|
constant BITS_PER_LED: natural := 24;
|
||||||
|
subtype color_t is std_logic_vector(BITS_PER_LED-1 downto 0);
|
||||||
|
type strand_store_t is array(0 to MAX_STRAND_LEN) of color_t;
|
||||||
|
signal strand_store: strand_store_t;
|
||||||
|
|
||||||
|
signal led_addr : std_logic_vector(7 downto 0);
|
||||||
|
signal current_color : color_t;
|
||||||
begin
|
begin
|
||||||
process(clk)
|
ws2812_inst: entity work.ws2812
|
||||||
|
generic map (
|
||||||
|
NUM_LEDS => MAX_STRAND_LEN,
|
||||||
|
COLOR_ORDER => "GRB",
|
||||||
|
T_CLK => 12.5 ns,
|
||||||
|
|
||||||
|
T0H => 0.35 us,
|
||||||
|
T0L => 0.8 us,
|
||||||
|
T1H => 0.7 us,
|
||||||
|
T1L => 0.6 us,
|
||||||
|
|
||||||
|
T_RES => 80 us
|
||||||
|
)
|
||||||
|
port map (
|
||||||
|
n_reset => not reset,
|
||||||
|
clk => clk,
|
||||||
|
|
||||||
|
led_addr => led_addr,
|
||||||
|
|
||||||
|
led_red => current_color(7 downto 0),
|
||||||
|
led_green => current_color(15 downto 8),
|
||||||
|
led_blue => current_color(23 downto 16),
|
||||||
|
|
||||||
|
dout => driver_out
|
||||||
|
);
|
||||||
|
|
||||||
|
-- https://github.com/YosysHQ/yosys/issues/3360
|
||||||
|
drivers <= (19 => driver_out, others => '0');
|
||||||
|
|
||||||
|
writer: process(clk)
|
||||||
|
variable store_counter: natural range 0 to MAX_STRAND_LEN-1;
|
||||||
begin
|
begin
|
||||||
if rising_edge(clk) then
|
if rising_edge(clk) then
|
||||||
if count = 10000000 then
|
current_color <= strand_store(to_integer(unsigned(led_addr)));
|
||||||
count <= 0;
|
|
||||||
count2 <= count2 + 1;
|
if udp_valid then
|
||||||
else
|
strand_store(store_counter) <= udp_data(23 downto 0);
|
||||||
count <= count + 1;
|
|
||||||
|
if udp_last then
|
||||||
|
store_counter := 0;
|
||||||
|
elsif store_counter /= MAX_STRAND_LEN-1 then
|
||||||
|
store_counter := store_counter + 1;
|
||||||
|
end if;
|
||||||
end if;
|
end if;
|
||||||
end if;
|
end if;
|
||||||
end process;
|
end process;
|
||||||
|
|
||||||
drivers <= (15 downto 4 => '0') & std_logic_vector(count2);
|
udp_ready <= '1';
|
||||||
end architecture;
|
end architecture;
|
||||||
|
|
Loading…
Reference in a new issue