framebuffer: larger counters
[litex.git] / milkymist / framebuffer / __init__.py
1 from migen.fhdl.structure import *
2 from migen.fhdl.specials import Instance
3 from migen.fhdl.module import Module
4 from migen.genlib.record import Record
5 from migen.flow.actor import *
6 from migen.flow.network import *
7 from migen.flow.transactions import *
8 from migen.flow import plumbing
9 from migen.actorlib import misc, dma_asmi, structuring, sim, spi
10
11 _hbits = 11
12 _vbits = 12
13
14 _bpp = 32
15 _bpc = 10
16 _pixel_layout_s = [
17 ("pad", _bpp-3*_bpc),
18 ("r", _bpc),
19 ("g", _bpc),
20 ("b", _bpc)
21 ]
22 _pixel_layout = [
23 ("p0", _pixel_layout_s),
24 ("p1", _pixel_layout_s)
25 ]
26
27 _bpc_dac = 8
28 _dac_layout_s = [
29 ("r", _bpc_dac),
30 ("g", _bpc_dac),
31 ("b", _bpc_dac)
32 ]
33 _dac_layout = [
34 ("hsync", 1),
35 ("vsync", 1),
36 ("p0", _dac_layout_s),
37 ("p1", _dac_layout_s)
38 ]
39
40 class _FrameInitiator(spi.SingleGenerator):
41 def __init__(self, asmi_bits, length_bits, alignment_bits):
42 layout = [
43 ("hres", _hbits, 640, 1),
44 ("hsync_start", _hbits, 656, 1),
45 ("hsync_end", _hbits, 752, 1),
46 ("hscan", _hbits, 800, 1),
47
48 ("vres", _vbits, 480),
49 ("vsync_start", _vbits, 492),
50 ("vsync_end", _vbits, 494),
51 ("vscan", _vbits, 525),
52
53 ("base", asmi_bits, 0, alignment_bits),
54 ("length", length_bits, 640*480*4, alignment_bits)
55 ]
56 spi.SingleGenerator.__init__(self, layout, spi.MODE_CONTINUOUS)
57
58 class VTG(Module, Actor):
59 def __init__(self):
60 Actor.__init__(self,
61 ("timing", Sink, [
62 ("hres", _hbits),
63 ("hsync_start", _hbits),
64 ("hsync_end", _hbits),
65 ("hscan", _hbits),
66 ("vres", _vbits),
67 ("vsync_start", _vbits),
68 ("vsync_end", _vbits),
69 ("vscan", _vbits)]),
70 ("pixels", Sink, _pixel_layout),
71 ("dac", Source, _dac_layout)
72 )
73
74 hactive = Signal()
75 vactive = Signal()
76 active = Signal()
77
78 generate_en = Signal()
79 hcounter = Signal(_hbits)
80 vcounter = Signal(_vbits)
81
82 skip = _bpc - _bpc_dac
83 self.comb += [
84 active.eq(hactive & vactive),
85 If(active,
86 [getattr(getattr(self.token("dac"), p), c).eq(getattr(getattr(self.token("pixels"), p), c)[skip:])
87 for p in ["p0", "p1"] for c in ["r", "g", "b"]]
88 ),
89
90 generate_en.eq(self.endpoints["timing"].stb & (~active | self.endpoints["pixels"].stb)),
91 self.endpoints["pixels"].ack.eq(self.endpoints["dac"].ack & active),
92 self.endpoints["dac"].stb.eq(generate_en)
93 ]
94 tp = self.token("timing")
95 self.sync += [
96 self.endpoints["timing"].ack.eq(0),
97 If(generate_en & self.endpoints["dac"].ack,
98 hcounter.eq(hcounter + 1),
99
100 If(hcounter == 0, hactive.eq(1)),
101 If(hcounter == tp.hres, hactive.eq(0)),
102 If(hcounter == tp.hsync_start, self.token("dac").hsync.eq(1)),
103 If(hcounter == tp.hsync_end, self.token("dac").hsync.eq(0)),
104 If(hcounter == tp.hscan,
105 hcounter.eq(0),
106 If(vcounter == tp.vscan,
107 vcounter.eq(0),
108 self.endpoints["timing"].ack.eq(1)
109 ).Else(
110 vcounter.eq(vcounter + 1)
111 )
112 ),
113
114 If(vcounter == 0, vactive.eq(1)),
115 If(vcounter == tp.vres, vactive.eq(0)),
116 If(vcounter == tp.vsync_start, self.token("dac").vsync.eq(1)),
117 If(vcounter == tp.vsync_end, self.token("dac").vsync.eq(0))
118 )
119 ]
120
121 class FIFO(Module, Actor):
122 def __init__(self):
123 Actor.__init__(self, ("dac", Sink, _dac_layout))
124
125 self.vga_hsync_n = Signal()
126 self.vga_vsync_n = Signal()
127 self.vga_r = Signal(_bpc_dac)
128 self.vga_g = Signal(_bpc_dac)
129 self.vga_b = Signal(_bpc_dac)
130
131 ###
132
133 data_width = 2+2*3*_bpc_dac
134 fifo_full = Signal()
135 fifo_write_en = Signal()
136 fifo_read_en = Signal()
137 fifo_data_out = Signal(data_width)
138 fifo_data_in = Signal(data_width)
139 self.specials += Instance("asfifo",
140 Instance.Parameter("data_width", data_width),
141 Instance.Parameter("address_width", 8),
142
143 Instance.Output("data_out", fifo_data_out),
144 Instance.Output("empty"),
145 Instance.Input("read_en", fifo_read_en),
146 Instance.Input("clk_read", ClockSignal("vga")),
147
148 Instance.Input("data_in", fifo_data_in),
149 Instance.Output("full", fifo_full),
150 Instance.Input("write_en", fifo_write_en),
151 Instance.Input("clk_write", ClockSignal()),
152
153 Instance.Input("rst", 0))
154 fifo_in = self.token("dac")
155 fifo_out = Record(_dac_layout)
156 self.comb += [
157 self.endpoints["dac"].ack.eq(~fifo_full),
158 fifo_write_en.eq(self.endpoints["dac"].stb),
159 fifo_data_in.eq(Cat(*fifo_in.flatten())),
160 Cat(*fifo_out.flatten()).eq(fifo_data_out),
161 self.busy.eq(0)
162 ]
163
164 pix_parity = Signal()
165 self.sync.vga += [
166 pix_parity.eq(~pix_parity),
167 self.vga_hsync_n.eq(~fifo_out.hsync),
168 self.vga_vsync_n.eq(~fifo_out.vsync),
169 If(pix_parity,
170 self.vga_r.eq(fifo_out.p1.r),
171 self.vga_g.eq(fifo_out.p1.g),
172 self.vga_b.eq(fifo_out.p1.b)
173 ).Else(
174 self.vga_r.eq(fifo_out.p0.r),
175 self.vga_g.eq(fifo_out.p0.g),
176 self.vga_b.eq(fifo_out.p0.b)
177 )
178 ]
179 self.comb += fifo_read_en.eq(pix_parity)
180
181 def sim_fifo_gen():
182 while True:
183 t = Token("dac")
184 yield t
185 print("H/V:" + str(t.value["hsync"]) + str(t.value["vsync"])
186 + " " + str(t.value["r"]) + " " + str(t.value["g"]) + " " + str(t.value["b"]))
187
188 class Framebuffer(Module):
189 def __init__(self, pads, asmiport, simulation=False):
190 asmi_bits = asmiport.hub.aw
191 alignment_bits = bits_for(asmiport.hub.dw//8) - 1
192 length_bits = _hbits + _vbits + 2 - alignment_bits
193 pack_factor = asmiport.hub.dw//(2*_bpp)
194 packed_pixels = structuring.pack_layout(_pixel_layout, pack_factor)
195
196 fi = _FrameInitiator(asmi_bits, length_bits, alignment_bits)
197 adrloop = misc.IntSequence(length_bits, asmi_bits)
198 adrbuffer = AbstractActor(plumbing.Buffer)
199 dma = dma_asmi.Reader(asmiport)
200 datbuffer = AbstractActor(plumbing.Buffer)
201 cast = structuring.Cast(asmiport.hub.dw, packed_pixels, reverse_to=True)
202 unpack = structuring.Unpack(pack_factor, _pixel_layout)
203 vtg = VTG()
204 if simulation:
205 fifo = sim.SimActor(sim_fifo_gen(), ("dac", Sink, _dac_layout))
206 else:
207 fifo = FIFO()
208
209 g = DataFlowGraph()
210 g.add_connection(fi, adrloop, source_subr=["length", "base"])
211 g.add_connection(adrloop, adrbuffer)
212 g.add_connection(adrbuffer, dma)
213 g.add_connection(dma, datbuffer)
214 g.add_connection(datbuffer, cast)
215 g.add_connection(cast, unpack)
216 g.add_connection(unpack, vtg, sink_ep="pixels")
217 g.add_connection(fi, vtg, sink_ep="timing", source_subr=[
218 "hres", "hsync_start", "hsync_end", "hscan",
219 "vres", "vsync_start", "vsync_end", "vscan"])
220 g.add_connection(vtg, fifo)
221 self.submodules._comp_actor = CompositeActor(g, debugger=False)
222
223 self._registers = fi.get_registers() + self._comp_actor.get_registers()
224
225 # Drive pads
226 if not simulation:
227 self.comb += [
228 pads.hsync_n.eq(fifo.vga_hsync_n),
229 pads.vsync_n.eq(fifo.vga_vsync_n),
230 pads.r.eq(fifo.vga_r),
231 pads.g.eq(fifo.vga_g),
232 pads.b.eq(fifo.vga_b)
233 ]
234 self.comb += pads.psave_n.eq(1)
235
236 def get_registers(self):
237 return self._registers