|
2 | 2 | from math import gcd |
3 | 3 |
|
4 | 4 |
|
5 | | -__all__ = ["MultiReg", "ResetSynchronizer", "PulseSynchronizer", "Gearbox"] |
| 5 | +__all__ = [ |
| 6 | + "MultiReg", |
| 7 | + "ResetSynchronizer", |
| 8 | + "PulseSynchronizer", |
| 9 | + "BusSynchronizer", |
| 10 | + "Gearbox" |
| 11 | +] |
6 | 12 |
|
7 | 13 |
|
8 | 14 | def _incr(signal, modulo): |
@@ -129,7 +135,6 @@ class PulseSynchronizer: |
129 | 135 |
|
130 | 136 | Parameters |
131 | 137 | ---------- |
132 | | -
|
133 | 138 | idomain : str |
134 | 139 | Name of input clock domain. |
135 | 140 | odomain : str |
@@ -164,6 +169,96 @@ def elaborate(self, platform): |
164 | 169 |
|
165 | 170 | return m |
166 | 171 |
|
| 172 | +class BusSynchronizer: |
| 173 | + """Pass a multi-bit signal safely between clock domains. |
| 174 | +
|
| 175 | + Ensures that all bits presented at `o` form a single word |
| 176 | + that was present synchronously at `i` in the input clock |
| 177 | + domain (unlike direct use of MultiReg). |
| 178 | +
|
| 179 | + Parameters |
| 180 | + ---------- |
| 181 | + width : int > 0 |
| 182 | + Width of the bus to be synchronised |
| 183 | + idomain : str |
| 184 | + Name of input clock domain |
| 185 | + odomain : str |
| 186 | + Name of output clock domain |
| 187 | + sync_stages : int > 1 |
| 188 | + Number of synchronisation stages used in the req/ack |
| 189 | + pulse synchronisers. Lower than 2 is unsafe. Higher |
| 190 | + values increase safety for high-frequency designs, |
| 191 | + but increase latency too. |
| 192 | + timeout : int >= 0 |
| 193 | + The request from idomain is re-sent if `timeout` cycles |
| 194 | + elapse without a response. |
| 195 | + `timeout` = 0 disables this feature. |
| 196 | +
|
| 197 | + Attributes |
| 198 | + ---------- |
| 199 | + i : Signal(width), in |
| 200 | + Input signal, sourced from `idomain` |
| 201 | + o : Signal(width), out |
| 202 | + Resynchronised version of `i`, driven to `odomain` |
| 203 | + """ |
| 204 | + def __init__(self, width, idomain, odomain, sync_stages=2, timeout = 127): |
| 205 | + if not isinstance(width, int) or width < 1: |
| 206 | + raise TypeError("width must be a positive integer, not '{!r}'".format(width)) |
| 207 | + if not isinstance(sync_stages, int) or sync_stages < 2: |
| 208 | + raise TypeError("sync_stages must be an integer > 1, not '{!r}'".format(sync_stages)) |
| 209 | + if not isinstance(timeout, int) or timeout < 0: |
| 210 | + raise TypeError("timeout must be a non-negative integer, not '{!r}'".format(timeout)) |
| 211 | + |
| 212 | + self.i = Signal(width) |
| 213 | + self.o = Signal(width, attrs={"no_retiming": True}) |
| 214 | + self.width = width |
| 215 | + self.idomain = idomain |
| 216 | + self.odomain = odomain |
| 217 | + self.sync_stages = sync_stages |
| 218 | + self.timeout = timeout |
| 219 | + |
| 220 | + def elaborate(self, platform): |
| 221 | + m = Module() |
| 222 | + if self.width == 1: |
| 223 | + m.submodules += MultiReg(self.i, self.o, odomain=self.odomain, n=self.sync_stages) |
| 224 | + return m |
| 225 | + |
| 226 | + req = Signal() |
| 227 | + ack_o = Signal() |
| 228 | + ack_i = Signal() |
| 229 | + |
| 230 | + sync_io = m.submodules.sync_io = \ |
| 231 | + PulseSynchronizer(self.idomain, self.odomain, self.sync_stages) |
| 232 | + sync_oi = m.submodules.sync_oi = \ |
| 233 | + PulseSynchronizer(self.odomain, self.idomain, self.sync_stages) |
| 234 | + |
| 235 | + if self.timeout != 0: |
| 236 | + countdown = Signal(max=self.timeout, reset=self.timeout) |
| 237 | + with m.If(ack_i | req): |
| 238 | + m.d[self.idomain] += countdown.eq(self.timeout) |
| 239 | + with m.Else(): |
| 240 | + m.d[self.idomain] += countdown.eq(countdown - countdown.bool()) |
| 241 | + |
| 242 | + start = Signal(reset=1) |
| 243 | + m.d[self.idomain] += start.eq(0) |
| 244 | + m.d.comb += [ |
| 245 | + req.eq(start | ack_i | (self.timeout != 0 and countdown == 0)), |
| 246 | + sync_io.i.eq(req), |
| 247 | + ack_o.eq(sync_io.o), |
| 248 | + sync_oi.i.eq(ack_o), |
| 249 | + ack_i.eq(sync_oi.o) |
| 250 | + ] |
| 251 | + |
| 252 | + buf_i = Signal(self.width, attrs={"no_retiming": True}) |
| 253 | + buf_o = Signal(self.width) |
| 254 | + with m.If(req): |
| 255 | + m.d[self.idomain] += buf_i.eq(self.i) |
| 256 | + sync_data = m.submodules.sync_data = \ |
| 257 | + MultiReg(buf_i, buf_o, odomain=self.odomain, n=self.sync_stages - 1) |
| 258 | + with m.If(ack_o): |
| 259 | + m.d[self.odomain] += self.o.eq(buf_o) |
| 260 | + |
| 261 | + return m |
167 | 262 |
|
168 | 263 | class Gearbox: |
169 | 264 | """Adapt the width of a continous datastream. |
|
0 commit comments