Skip to content

Commit 097b4b9

Browse files
committed
raster/persist: clean up, don't commit all writes with zero intensity
1 parent cd1da49 commit 097b4b9

File tree

1 file changed

+79
-65
lines changed

1 file changed

+79
-65
lines changed

gateware/src/tiliqua/raster/persist.py

Lines changed: 79 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,27 @@
1212
from amaranth_soc import csr, wishbone
1313

1414
from ..video.framebuffer import DMAFramebuffer
15+
from ..video.types import Pixel
1516

1617

1718
class Persistance(wiring.Component):
1819

1920
"""
2021
Read pixels from a framebuffer in PSRAM and apply gradual intensity reduction to simulate oscilloscope glow.
21-
Pixels are DMA'd from PSRAM as a wishbone master in bursts of 'fifo_depth // 2' in the 'sync' clock domain.
22+
Pixels are DMA'd from PSRAM as a wishbone master in bursts of 'fifo_depth' in the 'sync' clock domain.
2223
The block of pixels has its intensity reduced and is then DMA'd back to the bus.
2324
2425
'holdoff' is used to keep this core from saturating the bus between bursts.
2526
"""
2627

2728
def __init__(self, *, fb: DMAFramebuffer,
28-
fifo_depth=32, holdoff_default=256):
29+
fifo_depth=16, holdoff_default=256):
2930

3031
self.fb = fb
3132
self.fifo_depth = fifo_depth
3233

3334
# FIFO to cache pixels from PSRAM.
34-
self.fifo = SyncFIFOBuffered(width=32, depth=fifo_depth)
35-
36-
# Current addresses in the framebuffer (read and write sides)
37-
self.dma_addr_in = Signal(32, init=0)
38-
self.dma_addr_out = Signal(32)
35+
self.fifo = SyncFIFOBuffered(width=fb.bus.data_width, depth=fifo_depth)
3936

4037
super().__init__({
4138
# Tweakables
@@ -50,107 +47,124 @@ def __init__(self, *, fb: DMAFramebuffer,
5047
def elaborate(self, platform) -> Module:
5148
m = Module()
5249

53-
# Length of framebuffer in 32-bit words
54-
fb_len_words = (self.fb.timings.active_pixels * self.fb.bytes_per_pixel) // 4
55-
56-
holdoff_count = Signal(32)
57-
pnext = Signal(32)
58-
wr_source = Signal(32)
59-
6050
m.submodules.fifo = self.fifo
6151
bus = self.bus
62-
dma_addr_in = self.dma_addr_in
63-
dma_addr_out = self.dma_addr_out
6452

65-
decay_latch = Signal(4)
53+
# Length of framebuffer in bus words
54+
fb_len_words = ((self.fb.timings.active_pixels * self.fb.bytes_per_pixel) //
55+
(self.fb.bus.data_width // Pixel.as_shape().size))
56+
57+
# Track framebuffer position by tracking fifo reads/writes
58+
dma_offs_in = Signal(self.fb.bus.addr_width, init=0)
59+
with m.If(self.fifo.w_en & self.fifo.w_rdy):
60+
with m.If(dma_offs_in < (fb_len_words-1)):
61+
m.d.sync += dma_offs_in.eq(dma_offs_in + 1)
62+
with m.Else():
63+
m.d.sync += dma_offs_in.eq(0)
64+
65+
dma_offs_out = Signal.like(dma_offs_in)
66+
with m.If(self.fifo.r_en & self.fifo.r_rdy):
67+
with m.If(dma_offs_out < (fb_len_words-1)):
68+
m.d.sync += dma_offs_out.eq(dma_offs_out + 1)
69+
with m.Else():
70+
m.d.sync += dma_offs_out.eq(0)
71+
72+
# Latched version of decay speed control input
73+
decay_latch = Signal.like(self.decay)
74+
# Track delay between read/write bursts
75+
holdoff_count = Signal(32)
76+
# Incoming pixel array (read from FIFO)
77+
pixels_r = Signal(data.ArrayLayout(Pixel, 4))
78+
79+
m.d.comb += self.fifo.w_data.eq(bus.dat_r)
80+
81+
# Used for fastpath when all pixels are zero
82+
any_nonzero_reads = Signal()
83+
pixels_peek = Signal(data.ArrayLayout(Pixel, 4))
84+
m.d.comb += pixels_peek.eq(self.fifo.w_data)
85+
with m.If(self.fifo.w_en):
86+
with m.If((pixels_peek[0].intensity != 0) |
87+
(pixels_peek[1].intensity != 0) |
88+
(pixels_peek[2].intensity != 0) |
89+
(pixels_peek[3].intensity != 0)):
90+
m.d.sync += any_nonzero_reads.eq(1)
6691

67-
# Persistance state machine in 'sync' domain.
6892
with m.FSM() as fsm:
6993
with m.State('OFF'):
7094
with m.If(self.enable):
7195
m.next = 'BURST-IN'
7296

7397
with m.State('BURST-IN'):
74-
m.d.sync += holdoff_count.eq(0)
7598
m.d.sync += decay_latch.eq(self.decay)
7699
m.d.comb += [
77100
bus.stb.eq(1),
78101
bus.cyc.eq(1),
79102
bus.we.eq(0),
80103
bus.sel.eq(2**(bus.data_width//8)-1),
81-
bus.adr.eq(self.fb.fb_base + dma_addr_in),
104+
bus.adr.eq(self.fb.fb_base + dma_offs_in),
82105
self.fifo.w_en.eq(bus.ack),
83-
self.fifo.w_data.eq(bus.dat_r),
84106
bus.cti.eq(
85107
wishbone.CycleType.INCR_BURST),
86108
]
87-
with m.If(self.fifo.w_level >= (self.fifo_depth-1)):
109+
with m.If(self.fifo.w_level == (self.fifo_depth-1)):
88110
m.d.comb += bus.cti.eq(
89111
wishbone.CycleType.END_OF_BURST)
90-
with m.If(bus.stb & bus.ack & self.fifo.w_rdy): # WARN: drops last word
91-
with m.If(dma_addr_in < (fb_len_words-1)):
92-
m.d.sync += dma_addr_in.eq(dma_addr_in + 1)
93-
with m.Else():
94-
m.d.sync += dma_addr_in.eq(0)
95-
with m.Elif(~self.fifo.w_rdy):
96-
m.next = 'WAIT1'
97-
98-
with m.State('WAIT1'):
99-
m.d.sync += holdoff_count.eq(holdoff_count + 1)
100-
with m.If(holdoff_count > self.holdoff):
101-
m.d.sync += pnext.eq(self.fifo.r_data)
102-
m.d.comb += self.fifo.r_en.eq(1)
103-
m.next = 'BURST-OUT'
112+
m.next = 'PREFETCH'
104113

105-
with m.State('BURST-OUT'):
114+
with m.State('PREFETCH'):
115+
# Do not permit bus arbitration between burst in and out.
116+
m.d.comb += bus.cyc.eq(1)
117+
m.d.comb += self.fifo.w_en.eq(bus.ack)
106118
m.d.sync += holdoff_count.eq(0)
119+
with m.If(~bus.ack):
120+
with m.If(any_nonzero_reads):
121+
# Prefetch first FIFO entry before burst
122+
m.d.comb += self.fifo.r_en.eq(1)
123+
m.d.sync += pixels_r.eq(self.fifo.r_data)
124+
m.next = 'BURST-OUT'
125+
with m.Else():
126+
# Fastpath: all pixels have zero intensity -
127+
# no write is needed, skip it. This saves a lot
128+
# of bandwidth as the screen is mostly black.
129+
m.next = 'DRAIN'
107130

108-
# Incoming pixel array (read from FIFO)
109-
pixa = Signal(data.ArrayLayout(unsigned(8), 4))
110-
# Outgoing pixel array (write to bus)
111-
pixb = Signal(data.ArrayLayout(unsigned(8), 4))
112-
113-
m.d.sync += [
114-
pixa.eq(wr_source),
115-
]
116-
131+
with m.State('BURST-OUT'):
117132
# The actual persistance calculation. 4 pixels at a time.
133+
pixels_w = Signal(data.ArrayLayout(Pixel, 4))
118134
for n in range(4):
119135
# color
120-
m.d.comb += pixb[n][0:4].eq(pixa[n][0:4])
136+
m.d.comb += pixels_w[n].color.eq(pixels_r[n].color)
121137
# intensity
122-
with m.If(pixa[n][4:8] >= decay_latch):
123-
m.d.comb += pixb[n][4:8].eq(pixa[n][4:8] - decay_latch)
138+
with m.If(pixels_r[n].intensity >= decay_latch):
139+
m.d.comb += pixels_w[n].intensity.eq(pixels_r[n].intensity - decay_latch)
124140
with m.Else():
125-
m.d.comb += pixb[n][4:8].eq(0)
126-
141+
m.d.comb += pixels_w[n].intensity.eq(0)
127142

128143
m.d.comb += [
129144
bus.stb.eq(1),
130145
bus.cyc.eq(1),
131146
bus.we.eq(1),
132147
bus.sel.eq(2**(bus.data_width//8)-1),
133-
bus.adr.eq(self.fb.fb_base + dma_addr_out),
134-
wr_source.eq(pnext),
135-
bus.dat_w.eq(pixb),
148+
bus.adr.eq(self.fb.fb_base + dma_offs_out - 1),
149+
bus.dat_w.eq(pixels_w),
136150
bus.cti.eq(
137151
wishbone.CycleType.INCR_BURST)
138152
]
139-
with m.If(bus.stb & bus.ack):
153+
with m.If(bus.ack):
140154
m.d.comb += self.fifo.r_en.eq(1)
141-
m.d.comb += wr_source.eq(self.fifo.r_data),
142-
with m.If(dma_addr_out < (fb_len_words-1)):
143-
m.d.sync += dma_addr_out.eq(dma_addr_out + 1)
144-
m.d.comb += bus.adr.eq(self.fb.fb_base + dma_addr_out + 1),
145-
with m.Else():
146-
m.d.sync += dma_addr_out.eq(0)
147-
m.d.comb += bus.adr.eq(self.fb.fb_base + 0),
155+
m.d.sync += pixels_r.eq(self.fifo.r_data)
148156
with m.If(~self.fifo.r_rdy):
149157
m.d.comb += bus.cti.eq(
150158
wishbone.CycleType.END_OF_BURST)
151-
m.next = 'WAIT2'
159+
m.next = 'HOLDOFF'
160+
161+
with m.State('DRAIN'):
162+
m.d.comb += self.fifo.r_en.eq(1)
163+
with m.If(~self.fifo.r_rdy):
164+
m.next = 'HOLDOFF'
152165

153-
with m.State('WAIT2'):
166+
with m.State('HOLDOFF'):
167+
m.d.sync += any_nonzero_reads.eq(0)
154168
m.d.sync += holdoff_count.eq(holdoff_count + 1)
155169
with m.If(holdoff_count > self.holdoff):
156170
m.next = 'BURST-IN'
@@ -197,4 +211,4 @@ def elaborate(self, platform):
197211
with m.If(self._decay.f.decay.w_stb):
198212
m.d.sync += self.persist.decay.eq(self._decay.f.decay.w_data)
199213

200-
return m
214+
return m

0 commit comments

Comments
 (0)