|
4 | 4 | import re
|
5 | 5 |
|
6 | 6 | from ..hdl import *
|
| 7 | +from ..lib import io, wiring |
7 | 8 | from ..lib.cdc import ResetSynchronizer
|
8 | 9 | from ..build import *
|
9 | 10 |
|
10 | 11 | # Acknowledgments:
|
11 | 12 | # Parts of this file originate from https://github.com/tcjie/Gowin
|
12 | 13 |
|
13 | 14 |
|
| 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 | + |
14 | 163 | class GowinPlatform(TemplatedPlatform):
|
15 | 164 | """
|
16 | 165 | .. rubric:: Apicula toolchain
|
@@ -390,222 +539,18 @@ def create_missing_domain(self, name):
|
390 | 539 |
|
391 | 540 | return m
|
392 | 541 |
|
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) |
502 | 549 | 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