11import logging
22import asyncio
3+
34from amaranth import *
4- from amaranth .lib import io
5+ from amaranth .lib import io , wiring , stream
6+ from amaranth .lib .wiring import In
57
6- from ....support .endpoint import *
7- from ....gateware .pll import *
8- from ... import *
8+ from glasgow .abstract import AbstractAssembly , GlasgowPin , PortGroup
9+ from glasgow .applet import GlasgowAppletV2
10+ from glasgow .support .endpoint import *
11+ from glasgow .gateware .pll import *
912
1013
1114class VideoWS2812Output (Elaboratable ):
12- def __init__ (self , ports ):
15+ def __init__ (self , ports : PortGroup ):
1316 self .ports = ports
1417 self .out = Signal (len (ports .out ))
1518
@@ -22,14 +25,19 @@ def elaborate(self, platform):
2225 return m
2326
2427
25- class VideoWS2812OutputSubtarget (Elaboratable ):
26- def __init__ (self , ports , count , pix_in_size , pix_out_size , pix_format_func , out_fifo ):
27- self .ports = ports
28- self .count = count
29- self .pix_in_size = pix_in_size
30- self .pix_out_size = pix_out_size
28+ class VideoWS2812OutputComponent (wiring .Component ):
29+ i_stream : In (stream .Signature (8 ))
30+
31+ def __init__ (
32+ self , ports : PortGroup , count : int , pix_in_size : int , pix_out_size : int , pix_format_func
33+ ):
34+ self .ports = ports
35+ self .count = count
36+ self .pix_in_size = pix_in_size
37+ self .pix_out_size = pix_out_size
3138 self .pix_format_func = pix_format_func
32- self .out_fifo = out_fifo
39+
40+ super ().__init__ ()
3341
3442 def elaborate (self , platform ):
3543 # Safe timings:
@@ -54,29 +62,29 @@ def elaborate(self, platform):
5462 pix_out_size = self .pix_out_size
5563 pix_out_bpp = pix_out_size * 8
5664
57- cyc_ctr = Signal (range (t_reset + 1 ))
58- bit_ctr = Signal (range (pix_out_bpp + 1 ))
59- byt_ctr = Signal (range ((pix_in_size )+ 1 ))
60- pix_ctr = Signal (range (self .count + 1 ))
65+ cyc_ctr = Signal (range (t_reset + 1 ))
66+ bit_ctr = Signal (range (pix_out_bpp + 1 ))
67+ byt_ctr = Signal (range ((pix_in_size ) + 1 ))
68+ pix_ctr = Signal (range (self .count + 1 ))
6169 word_ctr = Signal (range (max (2 , len (self .ports .out ))))
6270
63- pix = Array ([ Signal (8 ) for i in range ((pix_in_size ) - 1 ) ])
71+ pix = Array ([Signal (8 ) for i in range ((pix_in_size ) - 1 )])
6472 word = Signal (pix_out_bpp * len (self .ports .out ))
6573
6674 with m .FSM ():
6775 with m .State ("LOAD" ):
6876 m .d .comb += [
69- self .out_fifo . r_en .eq (1 ),
77+ self .i_stream . ready .eq (1 ),
7078 output .out .eq (0 ),
7179 ]
72- with m .If (self .out_fifo . r_rdy ):
80+ with m .If (self .i_stream . valid ):
7381 with m .If (byt_ctr < ((pix_in_size ) - 1 )):
7482 m .d .sync += [
75- pix [byt_ctr ].eq (self .out_fifo . r_data ),
83+ pix [byt_ctr ].eq (self .i_stream . payload ),
7684 byt_ctr .eq (byt_ctr + 1 ),
7785 ]
7886 with m .Else ():
79- p = self .pix_format_func (* pix , self .out_fifo . r_data )
87+ p = self .pix_format_func (* pix , self .i_stream . payload )
8088 m .d .sync += word .eq (Cat (word [pix_out_bpp :], p ))
8189 with m .If (word_ctr < (len (self .ports .out ) - 1 )):
8290 m .d .sync += [
@@ -91,8 +99,10 @@ def elaborate(self, platform):
9199 m .d .comb += output .out .eq ((1 << len (self .ports .out )) - 1 )
92100 m .d .sync += cyc_ctr .eq (cyc_ctr + 1 )
93101 with m .Elif (cyc_ctr < t_one ):
94- m .d .comb += (o .eq (word [(pix_out_bpp - 1 ) + (pix_out_bpp * i )])
95- for i ,o in enumerate (output .out ))
102+ m .d .comb += (
103+ o .eq (word [(pix_out_bpp - 1 ) + (pix_out_bpp * i )])
104+ for i , o in enumerate (output .out )
105+ )
96106 m .d .sync += cyc_ctr .eq (cyc_ctr + 1 )
97107 with m .Elif (cyc_ctr < t_period ):
98108 m .d .comb += output .out .eq (0 )
@@ -135,7 +145,34 @@ def elaborate(self, platform):
135145 return m
136146
137147
138- class VideoWS2812OutputApplet (GlasgowApplet ):
148+ class VideoWS2812OutputInterface :
149+ def __init__ (
150+ self ,
151+ logger : logging .Logger ,
152+ assembly : AbstractAssembly ,
153+ * ,
154+ out : tuple [GlasgowPin ],
155+ count : int ,
156+ pix_in_size : int ,
157+ pix_out_size : int ,
158+ pix_format_func ,
159+ buffer : int ,
160+ ):
161+ self ._logger = logger
162+ ports = assembly .add_port_group (out = out )
163+ component = assembly .add_submodule (
164+ VideoWS2812OutputComponent (ports , count , pix_in_size , pix_out_size , pix_format_func )
165+ )
166+ self ._pipe = assembly .add_out_pipe (
167+ component .i_stream , buffer_size = len (out ) * count * pix_in_size * buffer
168+ )
169+
170+ async def write (self , data ):
171+ await self ._pipe .send (data )
172+ await self ._pipe .flush ()
173+
174+
175+ class VideoWS2812OutputApplet (GlasgowAppletV2 ):
139176 logger = logging .getLogger (__name__ )
140177 help = "display video via WS2812 LEDs"
141178 description = """
@@ -152,66 +189,61 @@ class VideoWS2812OutputApplet(GlasgowApplet):
152189
153190 @classmethod
154191 def add_build_arguments (cls , parser , access ):
155- super ().add_build_arguments (parser , access )
156-
192+ access .add_voltage_argument (parser )
157193 access .add_pins_argument (parser , "out" , width = range (1 , 17 ), required = True )
158194 parser .add_argument (
159195 "-c" , "--count" , metavar = "N" , type = int , required = True ,
160196 help = "set the number of LEDs per string" )
161197 parser .add_argument (
162198 "-f" , "--pix-fmt" , metavar = "F" , choices = cls .pixel_formats .keys (), default = "RGB-BRG" ,
163199 help = "set the pixel format (one of: %(choices)s, default: %(default)s)" )
164-
165- def build (self , target , args ):
166- self .pix_in_size , pix_out_size , pix_format_func = self .pixel_formats [args .pix_fmt ]
167-
168- self .mux_interface = iface = target .multiplexer .claim_interface (self , args )
169- subtarget = iface .add_subtarget (VideoWS2812OutputSubtarget (
170- ports = iface .get_port_group (out = args .out ),
171- count = args .count ,
172- pix_in_size = self .pix_in_size ,
173- pix_out_size = pix_out_size ,
174- pix_format_func = pix_format_func ,
175- out_fifo = iface .get_out_fifo (),
176- ))
177-
178- return subtarget
179-
180- @classmethod
181- def add_run_arguments (cls , parser , access ):
182- super ().add_run_arguments (parser , access )
183-
184200 parser .add_argument (
185201 "-b" , "--buffer" , metavar = "N" , type = int , default = 16 ,
186202 help = "set the number of frames to buffer internally (buffered twice)" )
187203
188- async def run (self , device , args ):
189- buffer_size = len (args .out ) * args .count * self .pix_in_size * args .buffer
190- return await device .demultiplexer .claim_interface (self , self .mux_interface , args ,
191- write_buffer_size = buffer_size )
204+ def build (self , args ):
205+ self .pix_in_size , pix_out_size , pix_format_func = self .pixel_formats [args .pix_fmt ]
206+
207+ with self .assembly .add_applet (self ):
208+ self .assembly .use_voltage (args .voltage )
209+ self .ws2812_iface = VideoWS2812OutputInterface (
210+ self .logger ,
211+ self .assembly ,
212+ out = args .out ,
213+ count = args .count ,
214+ pix_in_size = self .pix_in_size ,
215+ pix_out_size = pix_out_size ,
216+ pix_format_func = pix_format_func ,
217+ buffer = args .buffer ,
218+ )
192219
193220 @classmethod
194- def add_interact_arguments (cls , parser ):
221+ def add_run_arguments (cls , parser ):
195222 ServerEndpoint .add_argument (parser , "endpoint" )
196223
197- async def interact (self , device , args , leds ):
224+ async def run (self , args ):
198225 frame_size = len (args .out ) * args .count * self .pix_in_size
199226 buffer_size = frame_size * args .buffer
200- endpoint = await ServerEndpoint ("socket" , self .logger , args .endpoint ,
201- queue_size = buffer_size , deprecated_cancel_on_eof = True )
227+ endpoint = await ServerEndpoint (
228+ "socket" ,
229+ self .logger ,
230+ args .endpoint ,
231+ queue_size = buffer_size ,
232+ deprecated_cancel_on_eof = True ,
233+ )
202234 while True :
203235 try :
204236 data = await asyncio .shield (endpoint .recv (buffer_size ))
205237 partial = len (data ) % frame_size
206238 while partial :
207239 data += await asyncio .shield (endpoint .recv (frame_size - partial ))
208240 partial = len (data ) % frame_size
209- await leds .write (data )
210- await leds .flush (wait = False )
241+ await self .ws2812_iface .write (data )
211242 except asyncio .CancelledError :
212243 pass
213244
214245 @classmethod
215246 def tests (cls ):
216247 from . import test
248+
217249 return test .VideoWS2812OutputAppletTestCase
0 commit comments