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