forked from GlasgowEmbedded/glasgow
/
fx2.py
197 lines (175 loc) · 6.85 KB
/
fx2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# Synchronous FIFO timings reference:
# http://www.cypress.com/file/138911/download#page=53
# All timings in ns referenced to positive edge of non-inverted IFCLK.
# "Int" means IFCLK sourced by FX2, "Ext" means IFCLK sourced by FPGA.
#
# Int Period Ext Period
# IFCLK >20.83 >20.83 <200
# IFCLK (48 MHz) 20.83
# IFCLK (30 MHz) 33.33
#
# Int S/H Ext S/H
# SLRD 18.7/0.0 12.7/3.7
# SLWR 10.4/0.0 12.1/3.6
# PKTEND 14.6/0.0 8.6/2.5
# FIFOADR 25.0/10.0
# FIFODATA 9.2/0.0 3.2/4.5
#
# Int Setup Ext Setup
# IFCLK->FLAG 9.5 13.5
# IFCLK->FIFODATA 11.0 15.0
# SLOE->FIFODATA 10.5
# FIFOADR->FLAG 10.7
# FIFOADR->FIFODATA 14.3
from migen import *
from migen.genlib.fsm import *
from migen.genlib.fifo import _FIFOInterface, AsyncFIFO, SyncFIFOBuffered
__all__ = ['FX2Arbiter']
class _DummyFIFO(Module, _FIFOInterface):
pass
class FX2Arbiter(Module):
"""
FX2 FIFO bus master.
Shuttles data between FX2 and FIFOs in bursts.
The arbiter supports up to four FIFOs organized as ``OUT, OUT, IN, IN``.
FIFOs that are never requested are not implemented and behave as if they
are never readable or writable.
"""
def __init__(self, fx2):
self.fx2 = fx2
self.out_fifos = Array([_DummyFIFO(width=8, depth=0) for _ in range(2)])
self. in_fifos = Array([_DummyFIFO(width=8, depth=0) for _ in range(2)])
self.streaming = Array([False for _ in range(2)])
def do_finalize(self):
fx2 = self.fx2
addr = Signal(2)
data = TSTriple(8)
sloe = Signal()
slrd = Signal()
slwr = Signal()
pend = Signal()
rdy = Signal(4)
self.comb += [
fx2.fifoadr.eq(addr),
rdy.eq(fx2.flag & Cat([fifo.writable for fifo in self.out_fifos] +
[fifo.readable for fifo in self.in_fifos])),
self.out_fifos[addr[0]].din.eq(data.i),
data.o.eq(self.in_fifos[addr[0]].dout),
fx2.sloe.eq(~sloe),
fx2.slrd.eq(~slrd),
fx2.slwr.eq(~slwr),
fx2.pktend.eq(~pend),
]
self.specials += \
data.get_tristate(fx2.fd)
# Calculate the address of the next ready FIFO in a round robin process.
naddr = Signal(2)
naddr_c = {}
for addr_v in range(2**addr.nbits):
for rdy_v in range(2**rdy.nbits):
for offset in range(2**addr.nbits):
naddr_v = (addr_v + offset) % 2**addr.nbits
if rdy_v & (1 << naddr_v):
break
else:
naddr_v = (addr_v + 1) % 2**addr.nbits
naddr_c[rdy_v|(addr_v<<rdy.nbits)] = naddr.eq(naddr_v)
self.comb += Case(Cat(rdy, addr), naddr_c)
self.submodules.fsm = FSM(reset_state="NEXT")
# SLOE to FIFODATA setup: 1 cycle
# FIFOADR to FIFODATA setup: 2 cycles
self.fsm.act("NEXT",
NextValue(addr, naddr),
If(rdy,
NextState("SETUP")
)
)
self.fsm.act("SETUP",
If(addr[1],
NextValue(sloe, 0),
NextState("SETUP-IN")
).Else(
NextValue(data.oe, 0),
NextState("SETUP-OUT")
)
)
self.fsm.act("SETUP-IN",
NextValue(data.oe, 1),
NextState("XFER-IN")
)
self.fsm.act("XFER-IN",
# This could have been written as
# If(rdy & (1 << addr)), slwr.eq(1), ...)
# but on sluggish UP5K fabric having fx2.flag in fx2.slwr combinatorial path
# does not clear timing and results in corrupted data being sent when the flag
# goes down (i.e. at the very end of the packet that fills the last of
# the FX2's available buffers).
If(self.in_fifos[addr[0]].readable,
slwr.eq(1),
self.in_fifos[addr[0]].re.eq(1),
If(~(fx2.flag & (1 << addr)),
NextState("XFER-IN-LAST")
)
).Else(
pend.eq(~self.streaming[addr[0]]),
NextState("NEXT")
)
)
self.fsm.act("XFER-IN-LAST",
If(self.in_fifos[addr[0]].readable,
slwr.eq(1),
self.in_fifos[addr[0]].re.eq(1),
NextState("NEXT")
).Elif(~self.streaming[addr[0]],
pend.eq(1),
NextState("NEXT")
).Else(
# We don't want to hold up the other FIFOs if the 512th byte doesn't arrive
# promptly, but glitches on the FLAGn pins tend to be disastrous for streaming
# applications, and FXMA108 seems to cause them a lot, so instead, accept that
# a streaming pipe that suddenly stops at 511th byte will cause a lockup
# (at least until something is done about signal integrity).
)
)
self.fsm.act("SETUP-OUT",
NextValue(sloe, 1),
NextState("XFER-OUT")
)
self.fsm.act("XFER-OUT",
If(rdy & (1 << addr),
slrd.eq(1),
self.out_fifos[addr[0]].we.eq(1)
).Else(
NextState("NEXT")
)
)
def _make_fifo(self, arbiter_side, logic_side, cd_logic, depth):
if cd_logic is None:
fifo = SyncFIFOBuffered(8, depth)
else:
assert isinstance(cd_logic, ClockDomain)
fifo = ClockDomainsRenamer({
arbiter_side: "sys",
logic_side: "logic",
})(AsyncFIFO(8, depth))
fifo.clock_domains.cd_logic = ClockDomain()
self.comb += fifo.cd_logic.clk.eq(cd_logic.clk)
if cd_logic.rst is not None:
self.comb += fifo.cd_logic.rst.eq(cd_logic.rst)
self.submodules += fifo
return fifo
def get_out_fifo(self, n, depth=512, clock_domain=None):
assert 0 <= n < 2
assert isinstance(self.out_fifos[n], _DummyFIFO)
fifo = self._make_fifo(arbiter_side="write", logic_side="read",
cd_logic=clock_domain, depth=depth)
self.out_fifos[n] = fifo
return fifo
def get_in_fifo(self, n, depth=512, streaming=True, clock_domain=None):
assert 0 <= n < 2
assert isinstance(self.in_fifos[n], _DummyFIFO)
fifo = self._make_fifo(arbiter_side="read", logic_side="write",
cd_logic=clock_domain, depth=depth)
self.in_fifos[n] = fifo
self.streaming[n] = streaming
return fifo