|
2 | 2 |
|
3 | 3 | from ..hdl import *
|
4 | 4 | from ..lib.cdc import ResetSynchronizer
|
| 5 | +from ..lib import io |
5 | 6 | from ..build import *
|
6 | 7 |
|
7 | 8 |
|
@@ -414,206 +415,162 @@ def create_missing_domain(self, name):
|
414 | 415 |
|
415 | 416 | return m
|
416 | 417 |
|
417 |
| - def should_skip_port_component(self, port, attrs, component): |
418 |
| - # On iCE40, a differential input is placed by only instantiating an SB_IO primitive for |
419 |
| - # the pin with z=0, which is the non-inverting pin. The pinout unfortunately differs |
420 |
| - # between LP/HX and UP series: |
421 |
| - # * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting) |
422 |
| - # * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting) |
423 |
| - if attrs.get("IO_STANDARD", "SB_LVCMOS") == "SB_LVDS_INPUT" and component == "n": |
424 |
| - return True |
425 |
| - return False |
426 |
| - |
427 |
| - def _get_io_buffer(self, m, pin, port, attrs, *, i_invert=False, o_invert=False, |
428 |
| - invert_lut=False): |
429 |
| - def get_dff(clk, d, q): |
| 418 | + def _get_io_buffer_single(self, buffer, port, *, invert_lut=False): |
| 419 | + def get_dff(domain, q, d): |
430 | 420 | for bit in range(len(d)):
|
431 | 421 | m.submodules += Instance("SB_DFF",
|
432 |
| - i_C=clk, |
| 422 | + i_C=ClockSignal(domain), |
433 | 423 | i_D=d[bit],
|
434 | 424 | o_Q=q[bit])
|
435 | 425 |
|
436 |
| - def get_ineg(y, invert): |
| 426 | + def get_inv(y, a): |
437 | 427 | if invert_lut:
|
438 |
| - a = Signal.like(y, name_suffix=f"_x{1 if invert else 0}") |
439 |
| - for bit in range(len(y)): |
| 428 | + for bit, inv in enumerate(port.invert): |
440 | 429 | m.submodules += Instance("SB_LUT4",
|
441 |
| - p_LUT_INIT=Const(0b01 if invert else 0b10, 16), |
| 430 | + p_LUT_INIT=Const(0b01 if inv else 0b10, 16), |
442 | 431 | i_I0=a[bit],
|
443 | 432 | i_I1=Const(0),
|
444 | 433 | i_I2=Const(0),
|
445 | 434 | i_I3=Const(0),
|
446 | 435 | o_O=y[bit])
|
447 |
| - return a |
448 |
| - elif invert: |
449 |
| - a = Signal.like(y, name_suffix="_n") |
450 |
| - m.d.comb += y.eq(~a) |
451 |
| - return a |
452 | 436 | else:
|
453 |
| - return y |
| 437 | + mask = sum(int(inv) << bit for bit, inv in enumerate(port.invert)) |
| 438 | + if mask == 0: |
| 439 | + m.d.comb += y.eq(a) |
| 440 | + elif mask == ((1 << len(port)) - 1): |
| 441 | + m.d.comb += y.eq(~a) |
| 442 | + else: |
| 443 | + m.d.comb += y.eq(a ^ mask) |
454 | 444 |
|
455 |
| - def get_oneg(a, invert): |
456 |
| - if invert_lut: |
457 |
| - y = Signal.like(a, name_suffix=f"_x{1 if invert else 0}") |
458 |
| - for bit in range(len(a)): |
459 |
| - m.submodules += Instance("SB_LUT4", |
460 |
| - p_LUT_INIT=Const(0b01 if invert else 0b10, 16), |
461 |
| - i_I0=a[bit], |
462 |
| - i_I1=Const(0), |
463 |
| - i_I2=Const(0), |
464 |
| - i_I3=Const(0), |
465 |
| - o_O=y[bit]) |
466 |
| - return y |
467 |
| - elif invert: |
468 |
| - y = Signal.like(a, name_suffix="_n") |
469 |
| - m.d.comb += y.eq(~a) |
470 |
| - return y |
471 |
| - else: |
472 |
| - return a |
| 445 | + m = Module() |
473 | 446 |
|
474 |
| - if "GLOBAL" in attrs: |
475 |
| - is_global_input = bool(attrs["GLOBAL"]) |
476 |
| - del attrs["GLOBAL"] |
| 447 | + if isinstance(buffer, io.DDRBuffer): |
| 448 | + if buffer.direction is not io.Direction.Output: |
| 449 | + # Re-register both inputs before they enter fabric. This increases hold time |
| 450 | + # to an entire cycle, and adds one cycle of latency. |
| 451 | + i0 = Signal(len(port)) |
| 452 | + i1 = Signal(len(port)) |
| 453 | + i0_neg = Signal(len(port)) |
| 454 | + i1_neg = Signal(len(port)) |
| 455 | + get_inv(i0_neg, i0) |
| 456 | + get_inv(i1_neg, i1) |
| 457 | + get_dff(buffer.i_domain, buffer.i[0], i0_neg) |
| 458 | + get_dff(buffer.i_domain, buffer.i[1], i1_neg) |
| 459 | + if buffer.direction is not io.Direction.Input: |
| 460 | + # Re-register negedge output after it leaves fabric. This increases setup time |
| 461 | + # to an entire cycle, and doesn't add latency. |
| 462 | + o0 = Signal(len(port)) |
| 463 | + o1 = Signal(len(port)) |
| 464 | + o1_ff = Signal(len(port)) |
| 465 | + get_dff(buffer.o_domain, o1_ff, buffer.o[1]) |
| 466 | + get_inv(o0, buffer.o[0]) |
| 467 | + get_inv(o1, o1_ff) |
477 | 468 | else:
|
478 |
| - is_global_input = False |
479 |
| - assert not (is_global_input and i_invert) |
480 |
| - |
481 |
| - if "i" in pin.dir: |
482 |
| - if pin.xdr < 2: |
483 |
| - pin_i = get_ineg(pin.i, i_invert) |
484 |
| - elif pin.xdr == 2: |
485 |
| - pin_i0 = get_ineg(pin.i0, i_invert) |
486 |
| - pin_i1 = get_ineg(pin.i1, i_invert) |
487 |
| - if "o" in pin.dir: |
488 |
| - if pin.xdr < 2: |
489 |
| - pin_o = get_oneg(pin.o, o_invert) |
490 |
| - elif pin.xdr == 2: |
491 |
| - pin_o0 = get_oneg(pin.o0, o_invert) |
492 |
| - pin_o1 = get_oneg(pin.o1, o_invert) |
493 |
| - |
494 |
| - if "i" in pin.dir and pin.xdr == 2: |
495 |
| - i0_ff = Signal.like(pin_i0, name_suffix="_ff") |
496 |
| - i1_ff = Signal.like(pin_i1, name_suffix="_ff") |
497 |
| - get_dff(pin.i_clk, i0_ff, pin_i0) |
498 |
| - get_dff(pin.i_clk, i1_ff, pin_i1) |
499 |
| - if "o" in pin.dir and pin.xdr == 2: |
500 |
| - o1_ff = Signal.like(pin_o1, name_suffix="_ff") |
501 |
| - get_dff(pin.o_clk, pin_o1, o1_ff) |
| 469 | + if buffer.direction is not io.Direction.Output: |
| 470 | + i = Signal(len(port)) |
| 471 | + get_inv(buffer.i, i) |
| 472 | + if buffer.direction is not io.Direction.Input: |
| 473 | + o = Signal(len(port)) |
| 474 | + get_inv(o, buffer.o) |
502 | 475 |
|
503 | 476 | for bit in range(len(port)):
|
| 477 | + attrs = port.io.metadata[bit].attrs |
| 478 | + |
| 479 | + is_global_input = bool(attrs.get("GLOBAL", False)) |
| 480 | + if buffer.direction is io.Direction.Output: |
| 481 | + is_global_input = False |
| 482 | + if is_global_input: |
| 483 | + if port.invert[bit]: |
| 484 | + raise ValueError("iCE40 global input buffer doesn't support inversion") |
| 485 | + if not isinstance(buffer, io.Buffer): |
| 486 | + raise ValueError("iCE40 global input buffer cannot be registered") |
| 487 | + |
504 | 488 | io_args = [
|
505 |
| - ("io", "PACKAGE_PIN", port[bit]), |
506 |
| - *(("p", key, value) for key, value in attrs.items()), |
| 489 | + ("io", "PACKAGE_PIN", port.io[bit]), |
| 490 | + *(("p", key, value) for key, value in attrs.items() if key != "GLOBAL"), |
507 | 491 | ]
|
508 | 492 |
|
509 |
| - if "i" not in pin.dir: |
| 493 | + if buffer.direction is io.Direction.Output: |
510 | 494 | # If no input pin is requested, it is important to use a non-registered input pin
|
511 | 495 | # type, because an output-only pin would not have an input clock, and if its input
|
512 | 496 | # is configured as registered, this would prevent a co-located input-capable pin
|
513 | 497 | # from using an input clock.
|
514 | 498 | i_type = 0b01 # PIN_INPUT
|
515 |
| - elif pin.xdr == 0: |
| 499 | + elif isinstance(buffer, io.Buffer): |
516 | 500 | i_type = 0b01 # PIN_INPUT
|
517 |
| - elif pin.xdr > 0: |
| 501 | + if is_global_input: |
| 502 | + io_args.append(("o", "GLOBAL_BUFFER_OUTPUT", i[bit])) |
| 503 | + else: |
| 504 | + io_args.append(("o", "D_IN_0", i[bit])) |
| 505 | + elif isinstance(buffer, io.FFBuffer): |
| 506 | + i_type = 0b00 # PIN_INPUT_REGISTERED aka PIN_INPUT_DDR |
| 507 | + io_args.append(("i", "INPUT_CLK", ClockSignal(buffer.i_domain))) |
| 508 | + io_args.append(("o", "D_IN_0", i[bit])) |
| 509 | + elif isinstance(buffer, io.DDRBuffer): |
518 | 510 | i_type = 0b00 # PIN_INPUT_REGISTERED aka PIN_INPUT_DDR
|
519 |
| - if "o" not in pin.dir: |
| 511 | + io_args.append(("i", "INPUT_CLK", ClockSignal(buffer.i_domain))) |
| 512 | + io_args.append(("o", "D_IN_0", i0[bit])) |
| 513 | + io_args.append(("o", "D_IN_1", i1[bit])) |
| 514 | + |
| 515 | + if buffer.direction is io.Direction.Input: |
520 | 516 | o_type = 0b0000 # PIN_NO_OUTPUT
|
521 |
| - elif pin.xdr == 0 and pin.dir == "o": |
522 |
| - o_type = 0b0110 # PIN_OUTPUT |
523 |
| - elif pin.xdr == 0: |
| 517 | + elif isinstance(buffer, io.Buffer): |
524 | 518 | o_type = 0b1010 # PIN_OUTPUT_TRISTATE
|
525 |
| - elif pin.xdr == 1 and pin.dir == "o": |
526 |
| - o_type = 0b0101 # PIN_OUTPUT_REGISTERED |
527 |
| - elif pin.xdr == 1: |
| 519 | + io_args.append(("i", "D_OUT_0", o[bit])) |
| 520 | + elif isinstance(buffer, io.FFBuffer): |
528 | 521 | o_type = 0b1101 # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED
|
529 |
| - elif pin.xdr == 2 and pin.dir == "o": |
530 |
| - o_type = 0b0100 # PIN_OUTPUT_DDR |
531 |
| - elif pin.xdr == 2: |
| 522 | + io_args.append(("i", "OUTPUT_CLK", ClockSignal(buffer.o_domain))) |
| 523 | + io_args.append(("i", "D_OUT_0", o[bit])) |
| 524 | + elif isinstance(buffer, io.DDRBuffer): |
532 | 525 | o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED
|
| 526 | + io_args.append(("i", "OUTPUT_CLK", ClockSignal(buffer.o_domain))) |
| 527 | + io_args.append(("i", "D_OUT_0", o0[bit])) |
| 528 | + io_args.append(("i", "D_OUT_1", o1[bit])) |
| 529 | + |
533 | 530 | io_args.append(("p", "PIN_TYPE", C((o_type << 2) | i_type, 6)))
|
534 | 531 |
|
535 |
| - if hasattr(pin, "i_clk"): |
536 |
| - io_args.append(("i", "INPUT_CLK", pin.i_clk)) |
537 |
| - if hasattr(pin, "o_clk"): |
538 |
| - io_args.append(("i", "OUTPUT_CLK", pin.o_clk)) |
539 |
| - |
540 |
| - if "i" in pin.dir: |
541 |
| - if pin.xdr == 0 and is_global_input: |
542 |
| - io_args.append(("o", "GLOBAL_BUFFER_OUTPUT", pin.i[bit])) |
543 |
| - elif pin.xdr < 2: |
544 |
| - io_args.append(("o", "D_IN_0", pin_i[bit])) |
545 |
| - elif pin.xdr == 2: |
546 |
| - # Re-register both inputs before they enter fabric. This increases hold time |
547 |
| - # to an entire cycle, and adds one cycle of latency. |
548 |
| - io_args.append(("o", "D_IN_0", i0_ff[bit])) |
549 |
| - io_args.append(("o", "D_IN_1", i1_ff[bit])) |
550 |
| - if "o" in pin.dir: |
551 |
| - if pin.xdr < 2: |
552 |
| - io_args.append(("i", "D_OUT_0", pin_o[bit])) |
553 |
| - elif pin.xdr == 2: |
554 |
| - # Re-register negedge output after it leaves fabric. This increases setup time |
555 |
| - # to an entire cycle, and doesn't add latency. |
556 |
| - io_args.append(("i", "D_OUT_0", pin_o0[bit])) |
557 |
| - io_args.append(("i", "D_OUT_1", o1_ff[bit])) |
558 |
| - |
559 |
| - if pin.dir in ("oe", "io"): |
560 |
| - io_args.append(("i", "OUTPUT_ENABLE", pin.oe)) |
| 532 | + if buffer.direction is not io.Direction.Input: |
| 533 | + io_args.append(("i", "OUTPUT_ENABLE", buffer.oe)) |
561 | 534 |
|
562 | 535 | if is_global_input:
|
563 |
| - m.submodules[f"{pin.name}_{bit}"] = Instance("SB_GB_IO", *io_args) |
| 536 | + m.submodules[f"buf{bit}"] = Instance("SB_GB_IO", *io_args) |
564 | 537 | else:
|
565 |
| - m.submodules[f"{pin.name}_{bit}"] = Instance("SB_IO", *io_args) |
| 538 | + m.submodules[f"buf{bit}"] = Instance("SB_IO", *io_args) |
566 | 539 |
|
567 |
| - def get_input(self, pin, port, attrs, invert): |
568 |
| - self._check_feature("single-ended input", pin, attrs, |
569 |
| - valid_xdrs=(0, 1, 2), valid_attrs=True) |
570 |
| - m = Module() |
571 |
| - self._get_io_buffer(m, pin, port.io, attrs, i_invert=invert) |
572 | 540 | return m
|
573 | 541 |
|
574 |
| - def get_output(self, pin, port, attrs, invert): |
575 |
| - self._check_feature("single-ended output", pin, attrs, |
576 |
| - valid_xdrs=(0, 1, 2), valid_attrs=True) |
577 |
| - m = Module() |
578 |
| - self._get_io_buffer(m, pin, port.io, attrs, o_invert=invert) |
579 |
| - return m |
580 |
| - |
581 |
| - def get_tristate(self, pin, port, attrs, invert): |
582 |
| - self._check_feature("single-ended tristate", pin, attrs, |
583 |
| - valid_xdrs=(0, 1, 2), valid_attrs=True) |
584 |
| - m = Module() |
585 |
| - self._get_io_buffer(m, pin, port.io, attrs, o_invert=invert) |
586 |
| - return m |
587 |
| - |
588 |
| - def get_input_output(self, pin, port, attrs, invert): |
589 |
| - self._check_feature("single-ended input/output", pin, attrs, |
590 |
| - valid_xdrs=(0, 1, 2), valid_attrs=True) |
591 |
| - m = Module() |
592 |
| - self._get_io_buffer(m, pin, port.io, attrs, i_invert=invert, o_invert=invert) |
593 |
| - return m |
594 |
| - |
595 |
| - def get_diff_input(self, pin, port, attrs, invert): |
596 |
| - self._check_feature("differential input", pin, attrs, |
597 |
| - valid_xdrs=(0, 1, 2), valid_attrs=True) |
598 |
| - m = Module() |
599 |
| - # See comment in should_skip_port_component above. |
600 |
| - self._get_io_buffer(m, pin, port.p, attrs, i_invert=invert) |
601 |
| - return m |
602 |
| - |
603 |
| - def get_diff_output(self, pin, port, attrs, invert): |
604 |
| - self._check_feature("differential output", pin, attrs, |
605 |
| - valid_xdrs=(0, 1, 2), valid_attrs=True) |
606 |
| - m = Module() |
607 |
| - # Note that the non-inverting output pin is not driven the same way as a regular |
608 |
| - # output pin. The inverter introduces a delay, so for a non-inverting output pin, |
609 |
| - # an identical delay is introduced by instantiating a LUT. This makes the waveform |
610 |
| - # perfectly symmetric in the xdr=0 case. |
611 |
| - self._get_io_buffer(m, pin, port.p, attrs, o_invert= invert, invert_lut=True) |
612 |
| - self._get_io_buffer(m, pin, port.n, attrs, o_invert=not invert, invert_lut=True) |
613 |
| - return m |
614 |
| - |
615 |
| - # Tristate bidirectional buffers are not supported on iCE40 because it requires external |
616 |
| - # termination, which is different for differential pins configured as inputs and outputs. |
| 542 | + def get_io_buffer(self, buffer): |
| 543 | + if not isinstance(buffer, (io.Buffer, io.FFBuffer, io.DDRBuffer)): |
| 544 | + raise TypeError(f"Unknown IO buffer type {buffer!r}") |
| 545 | + if isinstance(buffer.port, io.DifferentialPort): |
| 546 | + port_p = io.SingleEndedPort(buffer.port.p, invert=buffer.port.invert, |
| 547 | + direction=buffer.port.direction) |
| 548 | + port_n = ~io.SingleEndedPort(buffer.port.n, invert=buffer.port.invert, |
| 549 | + direction=buffer.port.direction) |
| 550 | + if buffer.direction is io.Direction.Bidir: |
| 551 | + # Tristate bidirectional buffers are not supported on iCE40 because it requires |
| 552 | + # external termination, which is different for differential pins configured |
| 553 | + # as inputs and outputs. |
| 554 | + raise TypeError("iCE40 does not support bidirectional differential ports") |
| 555 | + elif buffer.direction is io.Direction.Output: |
| 556 | + m = Module() |
| 557 | + invert_lut = isinstance(buffer, io.Buffer) |
| 558 | + m.submodules.p = self._get_io_buffer_single(buffer, port_p, invert_lut=invert_lut) |
| 559 | + m.submodules.n = self._get_io_buffer_single(buffer, port_n, invert_lut=invert_lut) |
| 560 | + return m |
| 561 | + elif buffer.direction is io.Direction.Input: |
| 562 | + # On iCE40, a differential input is placed by only instantiating an SB_IO primitive |
| 563 | + # for the pin with z=0, which is the non-inverting pin. The pinout unfortunately |
| 564 | + # differs between LP/HX and UP series: |
| 565 | + # * for LP/HX, z=0 is DPxxB (B is non-inverting, A is inverting) |
| 566 | + # * for UP, z=0 is IOB_xxA (A is non-inverting, B is inverting) |
| 567 | + return self._get_io_buffer_single(buffer, port_p, invert_lut=invert_lut) |
| 568 | + else: |
| 569 | + assert False # :nocov: |
| 570 | + elif isinstance(buffer.port, io.SingleEndedPort): |
| 571 | + return self._get_io_buffer_single(buffer, buffer.port) |
| 572 | + else: |
| 573 | + raise TypeError(f"Unknown port type {buffer.port!r}") |
617 | 574 |
|
618 | 575 | # CDC primitives are not currently specialized for iCE40. It is not known if iCECube2 supports
|
619 | 576 | # the necessary attributes; nextpnr-ice40 does not.
|
0 commit comments