Skip to content

Commit 912bf45

Browse files
committed
vendor._gowin: implement lib.io buffer primitives.
1 parent 6fb5f3f commit 912bf45

File tree

1 file changed

+163
-218
lines changed

1 file changed

+163
-218
lines changed

amaranth/vendor/_gowin.py

+163-218
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,162 @@
44
import re
55

66
from ..hdl import *
7+
from ..lib import io, wiring
78
from ..lib.cdc import ResetSynchronizer
89
from ..build import *
910

1011
# Acknowledgments:
1112
# Parts of this file originate from https://github.com/tcjie/Gowin
1213

1314

15+
class InnerBuffer(wiring.Component):
16+
"""A private component used to implement ``lib.io`` buffers.
17+
18+
Works like ``lib.io.Buffer``, with the following differences:
19+
20+
- ``port.invert`` is ignored (handling the inversion is the outer buffer's responsibility)
21+
- ``t`` is per-pin inverted output enable
22+
"""
23+
def __init__(self, direction, port):
24+
self.direction = direction
25+
self.port = port
26+
members = {}
27+
if direction is not io.Direction.Output:
28+
members["i"] = wiring.In(len(port))
29+
if direction is not io.Direction.Input:
30+
members["o"] = wiring.Out(len(port))
31+
members["t"] = wiring.Out(len(port))
32+
super().__init__(wiring.Signature(members).flip())
33+
34+
def elaborate(self):
35+
m = Module()
36+
37+
for bit in range(len(self.port)):
38+
name = f"buf{bit}"
39+
if isinstance(self.port, io.SingleEndedPort):
40+
if self.direction is io.Direction.Input:
41+
m.submodules[name] = Instance("IBUF",
42+
i_I=self.port.io[bit],
43+
o_O=self.i[bit],
44+
)
45+
elif self.direction is io.Direction.Output:
46+
m.submodules[name] = Instance("TBUF",
47+
i_OEN=self.t[bit],
48+
i_I=self.o[bit],
49+
o_O=self.port.io[bit],
50+
)
51+
elif self.direction is io.Direction.Bidir:
52+
m.submodules[name] = Instance("IOBUF",
53+
i_OEN=self.t[bit],
54+
i_I=self.o[bit],
55+
o_O=self.i[bit],
56+
io_IO=self.port.io[bit],
57+
)
58+
else:
59+
assert False # :nocov:
60+
elif isinstance(self.port, io.DifferentialPort):
61+
if self.direction is io.Direction.Input:
62+
m.submodules[name] = Instance("TLVDS_IBUF",
63+
i_I=self.port.p[bit],
64+
i_IB=self.port.n[bit],
65+
o_O=self.i[bit],
66+
)
67+
elif self.direction is io.Direction.Output:
68+
m.submodules[name] = Instance("TLVDS_TBUF",
69+
i_OEN=self.t[bit],
70+
i_I=self.o[bit],
71+
o_O=self.port.p[bit],
72+
o_OB=self.port.n[bit],
73+
)
74+
elif self.direction is io.Direction.Bidir:
75+
m.submodules[name] = Instance("TLVDS_IOBUF",
76+
i_OEN=self.t[bit],
77+
i_I=self.o[bit],
78+
o_O=self.i[bit],
79+
io_IO=self.port.p[bit],
80+
io_IOB=self.port.n[bit],
81+
)
82+
else:
83+
assert False # :nocov:
84+
else:
85+
raise TypeError(f"Unknown port type {self.port!r}")
86+
87+
return m
88+
89+
90+
class IOBuffer(io.Buffer):
91+
def elaborate(self, platform):
92+
m = Module()
93+
94+
m.submodules.buf = buf = InnerBuffer(self.direction, self.port)
95+
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
96+
97+
if self.direction is not io.Direction.Output:
98+
m.d.comb += self.i.eq(buf.i ^ inv_mask)
99+
100+
if self.direction is not io.Direction.Input:
101+
m.d.comb += buf.o.eq(self.o ^ inv_mask)
102+
m.d.comb += buf.t.eq(~self.oe.replicate(len(self.port)))
103+
104+
return m
105+
106+
107+
class FFBuffer(io.FFBuffer):
108+
def elaborate(self, platform):
109+
m = Module()
110+
111+
m.submodules.buf = buf = InnerBuffer(self.direction, self.port)
112+
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
113+
114+
if self.direction is not io.Direction.Output:
115+
i_inv = Signal.like(self.i)
116+
m.d[self.i_domain] += i_inv.eq(buf.i)
117+
m.d.comb += self.i.eq(i_inv ^ inv_mask)
118+
119+
if self.direction is not io.Direction.Input:
120+
m.d[self.o_domain] += buf.o.eq(self.o ^ inv_mask)
121+
m.d[self.o_domain] += buf.t.eq(~self.oe.replicate(len(self.port)))
122+
123+
return m
124+
125+
126+
class DDRBuffer(io.DDRBuffer):
127+
def elaborate(self, platform):
128+
m = Module()
129+
130+
m.submodules.buf = buf = InnerBuffer(self.direction, self.port)
131+
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
132+
133+
if self.direction is not io.Direction.Output:
134+
i0_inv = Signal(len(self.port))
135+
i1_inv = Signal(len(self.port))
136+
for bit in range(len(self.port)):
137+
m.submodules.i_ddr = Instance("IDDR",
138+
i_CLK=ClockSignal(self.i_domain),
139+
i_D=buf.i[bit],
140+
o_Q0=i0_inv[bit],
141+
o_Q1=i1_inv[bit],
142+
)
143+
m.d.comb += self.i[0].eq(i0_inv ^ inv_mask)
144+
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
145+
146+
if self.direction is not io.Direction.Input:
147+
o0_inv = self.o[0] ^ inv_mask
148+
o1_inv = self.o[1] ^ inv_mask
149+
for bit in range(len(self.port)):
150+
m.submodules.o_ddr = Instance("ODDR",
151+
p_TXCLK_POL=0, # default -> Q1 changes on posedge of CLK
152+
i_CLK=ClockSignal(self.o_domain),
153+
i_D0=o0_inv[bit],
154+
i_D1=o1_inv[bit],
155+
i_TX=~self.oe,
156+
o_Q0=buf.o[bit],
157+
o_Q1=buf.t[bit],
158+
)
159+
160+
return m
161+
162+
14163
class GowinPlatform(TemplatedPlatform):
15164
"""
16165
.. rubric:: Apicula toolchain
@@ -390,222 +539,18 @@ def create_missing_domain(self, name):
390539

391540
return m
392541

393-
def _get_xdr_buffer(self, m, pin, i_invert=False, o_invert=False):
394-
395-
def get_ireg(clk,d,q):
396-
for bit in range(len(q)):
397-
m.submodules += Instance("DFF",
398-
i_CLK=clk,
399-
i_D=d[bit],
400-
o_Q=q[bit],
401-
)
402-
403-
def get_oreg(clk,d,q):
404-
for bit in range(len(q)):
405-
m.submodules += Instance("DFF",
406-
i_CLK=clk,
407-
i_D=d[bit],
408-
o_Q=q[bit]
409-
)
410-
411-
def get_iddr(clk,d,q0,q1):
412-
for bit in range(len(d)):
413-
m.submodules += Instance("IDDR",
414-
i_CLK=clk,
415-
i_D=d[bit],
416-
o_Q0=q0[bit],
417-
o_Q1=q1[bit]
418-
)
419-
420-
def get_oddr(clk,d0,d1,q):
421-
for bit in range(len(q)):
422-
m.submodules += Instance("ODDR",
423-
p_TXCLK_POL=0, # default -> Q1 changes on posedge of CLK
424-
i_CLK=clk,
425-
i_D0=d0[bit],
426-
i_D1=d1[bit],
427-
o_Q0=q[bit]
428-
)
429-
430-
def get_oeddr(clk,d0,d1,tx,q0,q1):
431-
for bit in range(len(q0)):
432-
m.submodules += Instance("ODDR",
433-
p_TXCLK_POL=0, # default -> Q1 changes on posedge of CLK
434-
i_CLK=clk,
435-
i_D0=d0[bit],
436-
i_D1=d1[bit],
437-
i_TX=tx,
438-
o_Q0=q0[bit],
439-
o_Q1=q1
440-
)
441-
442-
def get_ineg(y, invert):
443-
if invert:
444-
a = Signal.like(y, name_suffix="_n")
445-
m.d.comb += y.eq(~a)
446-
return a
447-
else:
448-
return y
449-
450-
def get_oneg(a, invert):
451-
if invert:
452-
y = Signal.like(a, name_suffix="_n")
453-
m.d.comb += y.eq(~a)
454-
return y
455-
else:
456-
return a
457-
458-
459-
if "i" in pin.dir:
460-
if pin.xdr < 2:
461-
pin_i = get_ineg(pin.i, i_invert)
462-
elif pin.xdr == 2:
463-
pin_i0 = get_ineg(pin.i0, i_invert)
464-
pin_i1 = get_ineg(pin.i1, i_invert)
465-
if "o" in pin.dir:
466-
if pin.xdr < 2:
467-
pin_o = get_oneg(pin.o, o_invert)
468-
elif pin.xdr == 2:
469-
pin_o0 = get_oneg(pin.o0, o_invert)
470-
pin_o1 = get_oneg(pin.o1, o_invert)
471-
472-
i = o = t = None
473-
474-
if "i" in pin.dir:
475-
i = Signal(pin.width, name=f"{pin.name}_xdr_i")
476-
if "o" in pin.dir:
477-
o = Signal(pin.width, name=f"{pin.name}_xdr_o")
478-
if pin.dir in ("oe", "io"):
479-
t = Signal(1, name=f"{pin.name}_xdr_t")
480-
481-
if pin.xdr == 0:
482-
if "i" in pin.dir:
483-
i = pin_i
484-
if "o" in pin.dir:
485-
o = pin_o
486-
if pin.dir in ("oe", "io"):
487-
t = ~pin.oe
488-
elif pin.xdr == 1:
489-
if "i" in pin.dir:
490-
get_ireg(pin.i_clk, i, pin_i)
491-
if "o" in pin.dir:
492-
get_oreg(pin.o_clk, pin_o, o)
493-
if pin.dir in ("oe", "io"):
494-
get_oreg(pin.o_clk, ~pin.oe, t)
495-
elif pin.xdr == 2:
496-
if "i" in pin.dir:
497-
get_iddr(pin.i_clk, i, pin_i0, pin_i1)
498-
if pin.dir in ("o",):
499-
get_oddr(pin.o_clk, pin_o0, pin_o1, o)
500-
if pin.dir in ("oe", "io"):
501-
get_oeddr(pin.o_clk, pin_o0, pin_o1, ~pin.oe, o, t)
542+
def get_io_buffer(self, buffer):
543+
if isinstance(buffer, io.Buffer):
544+
res = IOBuffer(buffer.direction, buffer.port)
545+
elif isinstance(buffer, io.FFBuffer):
546+
res = FFBuffer(buffer.direction, buffer.port)
547+
elif isinstance(buffer, io.DDRBuffer):
548+
res = DDRBuffer(buffer.direction, buffer.port)
502549
else:
503-
assert False
504-
505-
return (i, o, t)
506-
507-
def get_input(self, pin, port, attrs, invert):
508-
self._check_feature("single-ended input", pin, attrs,
509-
valid_xdrs=(0, 1, 2), valid_attrs=True)
510-
m = Module()
511-
i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert)
512-
for bit in range(pin.width):
513-
m.submodules[f"{pin.name}_{bit}"] = Instance("IBUF",
514-
i_I=port.io[bit],
515-
o_O=i[bit]
516-
)
517-
return m
518-
519-
def get_output(self, pin, port, attrs, invert):
520-
self._check_feature("single-ended output", pin, attrs,
521-
valid_xdrs=(0, 1, 2), valid_attrs=True)
522-
m = Module()
523-
i, o, t = self._get_xdr_buffer(m, pin, port.io, o_invert=invert)
524-
for bit in range(pin.width):
525-
m.submodules[f"{pin.name}_{bit}"] = Instance("OBUF",
526-
i_I=o[bit],
527-
o_O=port.io[bit]
528-
)
529-
return m
530-
531-
def get_tristate(self, pin, port, attrs, invert):
532-
self._check_feature("single-ended tristate", pin, attrs,
533-
valid_xdrs=(0, 1, 2), valid_attrs=True)
534-
m = Module()
535-
i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
536-
for bit in range(pin.width):
537-
m.submodules[f"{pin.name}_{bit}"] = Instance("TBUF",
538-
i_OEN=t,
539-
i_I=o[bit],
540-
o_O=port.io[bit]
541-
)
542-
return m
543-
544-
def get_input_output(self, pin, port, attrs, invert):
545-
self._check_feature("single-ended input/output", pin, attrs,
546-
valid_xdrs=(0, 1, 2), valid_attrs=True)
547-
m = Module()
548-
i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert, o_invert=invert)
549-
for bit in range(pin.width):
550-
m.submodules[f"{pin.name}_{bit}"] = Instance("IOBUF",
551-
i_OEN=t,
552-
i_I=o[bit],
553-
o_O=i[bit],
554-
io_IO=port.io[bit]
555-
)
556-
return m
557-
558-
def get_diff_input(self, pin, port, attrs, invert):
559-
self._check_feature("differential input", pin, attrs,
560-
valid_xdrs=(0, 1, 2), valid_attrs=True)
561-
m = Module()
562-
i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert)
563-
for bit in range(pin.wodth):
564-
m.submodules[f"{pin.name}_{bit}"] = Instance("TLVDS_IBUF",
565-
i_I=port.p[bit],
566-
i_IB=port.n[bit],
567-
o_O=i[bit]
568-
)
569-
return m
570-
571-
def get_diff_output(self, pin, port, attrs, invert):
572-
self._check_feature("differential output", pin, attrs,
573-
valid_xdrs=(0, 1, 2), valid_attrs=True)
574-
m = Module()
575-
i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
576-
for bit in range(pin.width):
577-
m.submodules[f"{pin.name}_{bit}"] = Instance("TLVDS_OBUF",
578-
i_I=o[bit],
579-
o_O=port.p[bit],
580-
o_OB=port.n[bit],
581-
)
582-
return m
583-
584-
def get_diff_tristate(self, pin, port, attrs, invert):
585-
self._check_feature("differential tristate", pin, attrs,
586-
valid_xdrs=(0, 1, 2), valid_attrs=True)
587-
m = Module()
588-
i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
589-
for bit in range(pin.width):
590-
m.submodules[f"{pin.name}_{bit}"] = Instance("TLVDS_TBUF",
591-
i_OEN=t,
592-
i_I=o[bit],
593-
o_O=port.p[bit],
594-
o_OB=port.n[bit]
595-
)
596-
return m
597-
598-
def get_diff_input_output(self, pin, port, attrs, invert):
599-
self._check_feature("differential input/output", pin, attrs,
600-
valid_xdrs=(0, 1, 2), valid_attrs=True)
601-
m = Module()
602-
i, o, t = self._get_xdr_buffer(m, pin, i_invert=invert, o_invert=invert)
603-
for bit in range(pin.width):
604-
m.submodules[f"{pin.name}_{bit}"] = Instance("TLVDS_IOBUF",
605-
i_OEN=t,
606-
i_I=o[bit],
607-
o_O=i[bit],
608-
io_IO=port.p[bit],
609-
io_IOB=port.n[bit]
610-
)
611-
return m
550+
raise TypeError(f"Unsupported buffer type {buffer!r}") # :nocov:
551+
if buffer.direction is not io.Direction.Output:
552+
res.i = buffer.i
553+
if buffer.direction is not io.Direction.Input:
554+
res.o = buffer.o
555+
res.oe = buffer.oe
556+
return res

0 commit comments

Comments
 (0)