Exemple #1
0
    def add_csrs(self):
        self._start = CSR()
        # CSR does not take a description parameter so we must set it manually
        self._start.description = "Writing to this register initializes payload execution"
        self._status = CSRStatus(fields=[
            CSRField("ready",
                     description="Indicates that the executor is not running"),
            CSRField(
                "overflow",
                description="Indicates the scratchpad memory address counter"
                " has overflown due to the number of READ commands sent during execution"
            ),
        ],
                                 description="Payload executor status register"
                                 )
        self._read_count = CSRStatus(
            len(self.scratchpad.counter),
            description="Number of data"
            " from READ commands that is stored in the scratchpad memory")

        self.comb += [
            self.start.eq(self._start.re),
            self._status.fields.ready.eq(self.ready),
            self._status.fields.overflow.eq(self.scratchpad.overflow),
            self._read_count.status.eq(self.scratchpad.counter),
        ]
Exemple #2
0
    def add_csrs(self):
        super().add_csrs()

        self._error_count = CSRStatus(size=len(self.error_count),
                                      description='Number of errors detected')
        self._skip_fifo = CSRStorage(
            description='Skip waiting for user to read the errors FIFO')
        self._error_offset = CSRStatus(
            size=len(self.mem_mask), description='Current offset of the error')
        self._error_data = CSRStatus(
            size=len(self.data_port.dat_r),
            description='Erroneous value read from DRAM memory')
        self._error_expected = CSRStatus(
            size=len(self.data_port.dat_r),
            description='Value expected to be read from DRAM memory')
        self._error_ready = CSRStatus(
            description='Error detected and ready to read')
        self._error_continue = CSR()
        self._error_continue.description = 'Continue reading until the next error'

        self.comb += [
            self._error_count.status.eq(self.error_count),
            self.skip_fifo.eq(self._skip_fifo.storage),
            self._error_offset.status.eq(self.error.offset),
            self._error_data.status.eq(self.error.data),
            self._error_expected.status.eq(self.error.expected),
            self.error.ready.eq(self._error_continue.re),
            self._error_ready.status.eq(self.error.valid),
        ]
Exemple #3
0
    def __init__(self):
        self.submodules.fifo = f = fifo.SyncFIFOBuffered(width=8, depth=64)
        in_reg = CSRStorage(8,
                            name="in",
                            description="""
                    Write half of the FIFO to send data out the Messible.
                    Writing to this register advances the write pointer automatically."""
                            )
        out_reg = CSRStatus(8,
                            name="out",
                            description="""
                    Read half of the FIFO to receive data on the Messible.
                    Reading from this register advances the read pointer automatically."""
                            )

        self.__setattr__("in", in_reg)
        self.__setattr__("out", out_reg)
        self.status = status = CSRStatus(fields=[
            CSRField(
                "full",
                description="``0`` if more data can fit into the IN FIFO."),
            CSRField(
                "have",
                description="``1`` if data can be read from the OUT FIFO."),
        ])

        self.intro = ModuleDoc("""
                Messible: An Ansible for Messages

                An Ansible is a system for instant communication across vast distances, from
                a small portable device to a huge terminal far away.  A Messible is a message-
                passing system from embedded devices to a host system.  You can use it to get
                very simple printf()-style support over a debug channel.

                The Messible assumes the host has a way to peek into the device's memory space.
                This is the case with the Wishbone bridge, which allows both the device and
                the host to access the same memory.

                At its core, a Messible is a FIFO.  As long as the ``STATUS.FULL`` bit is ``0``, the
                device can write data into the Messible by writing into the ``IN``.  However, if this
                value is ``1``, you need to decide if you want to wait for it to empty (if the other
                side is just slow), or if you want to drop the message.

                From the host side, you need to read ``STATUS.HAVE`` to see if there is data
                in the FIFO.  If there is, read ``OUT`` to get the most recent byte, which automatically
                advances the ``READ`` pointer.
                """)

        self.comb += [
            f.din.eq(in_reg.storage),
            f.we.eq(in_reg.re),
            out_reg.status.eq(f.dout),
            f.re.eq(out_reg.we),
            status.fields.full.eq(~f.writable),
            status.fields.have.eq(f.readable),
        ]
Exemple #4
0
    def __init__(self,
                 size=None,
                 *fields,
                 name=None,
                 reset=0,
                 description=None):
        """Create a memory-mapped DCSRStatus.

        Depending on the value of `readable` and `writeable`, Reg will be backed by
        either a :obj:`CSRStorage` or :obj:`CSRStatus`.  It will then create
        convenience wrappers around this core storage.

        It is possible to create registers that are both `readable` and `writeable`,
        in which case convenience methods will be prefixed with `i_` and `o_`.

        Args:
            size (int) (optional): How wide to make this register
                A width may be specified.  If none is specified, then the width is
                set to the total size of all `fields`.
                If no fields are specified, then the width must be specified.
            name (:obj:`str`): The name of register.
                The name should be all lowercase, and be a valid Python identifier.
                The name must not start with "_".  This will be transformed into
                all-caps for certain operations.  If no name is specified, :obj:`Reg`
                will attempt to infer it, but may raise an error if it can't figure out
                what to call itself.
            reset (:obj:`Signal(n)`): Value of the :obj:`CSRStatus` right after reset.
                The :obj:`CSRStatus` will immediately take its value after the first
                cycle, however it can be useful to provide an initialization value here
                for simulation purposes.
            description (:obj:`str`): An overview of this register
                This field contains Markdown data that describes this register's
                overall contents.
            *fields (:obj:`list` of :obj:`Field`): All fields of this register.
                Each entry in the list is a single :obj:`Field`.  The order of the
                fields represents the order in which they will be added to the
                :obj:`Reg`.  It is possible to have disjoint fields by setting the
                `offset` parameter, however it is an error to have overlapping
                :obj:`Field` regions.
                If there are no fields, then a single Field is created and the width
                must be specified.
        """

        (size, fields) = get_size_and_fields(size, fields, "status")
        bits = get_bit_list(fields)

        try:
            CSRStatus.__init__(self, len(bits), reset=reset, name=name)
        except Exception as e:
            raise ValueError(
                "Cannot extract CSRStatus name from code -- please provide one by passing `name=` to the initializer: {}"
                .format(e))
        self.make_status_signals(bits)
Exemple #5
0
    def __init__(self, k2mm: K2MM, dw=32):
        self.source_ctrl = Endpoint(k2mm.sink_tester_ctrl.description)
        self._probe_len = CSRStorage(description="Test frame length",
                                     fields=[
                                         CSRField(
                                             "length",
                                             size=16,
                                             description="Test frame length"),
                                     ],
                                     name="prb_length")
        self._probe_ctrl = CSRStorage(
            description="Test frame enable",
            fields=[CSRField("enable", size=1, description="Send test frame")],
            name="prb_ctrl")
        self._probe_status = CSRStatus(
            description="Probe status",
            fields=[
                CSRField("ready",
                         size=1,
                         description="1 = Test frame command ready"),
            ],
            name="prb_stat")

        self.comb += [
            self.source_ctrl.length.eq(self._probe_len.fields.length),
            self._probe_status.fields.ready.eq(self.source_ctrl.ready),
            self.source_ctrl.valid.eq(self._probe_ctrl.fields.enable
                                      & self._probe_ctrl.re)
        ]
Exemple #6
0
    def add_csrs(self):
        self._refresh_count = CSRStatus(
            len(self.refresh_counter.counter),
            description=
            "Count of all refresh commands issued (both by Memory Controller and Payload Executor)."
            " Value is latched from internal counter on mode trasition: MC -> PE or by writing to"
            " the `refresh_update` CSR.")
        self._at_refresh = CSRStorage(
            len(self.at_refresh),
            reset=0,
            description=
            "If set to a value different than 0 the mode transition MC -> PE will be peformed only"
            " when the value of this register matches the current refresh commands count."
        )
        self._refresh_update = CSR()
        self._refresh_update.description = "Force an update of the `refresh_count` CSR."

        self.comb += self.at_refresh.eq(self._at_refresh.storage)

        # detect mode transition
        pe_ongoing = self.fsm.ongoing("PAYLOAD-EXECUTION")
        mc_ongoing = self.fsm.ongoing("MEMORY-CONTROLLER")
        mc_ongoing_d = Signal()
        self.sync += mc_ongoing_d.eq(mc_ongoing)
        mc_to_pe = mc_ongoing_d & pe_ongoing

        self.sync += If(
            mc_to_pe | self._refresh_update.re,
            self._refresh_count.status.eq(self.refresh_counter.counter),
        )
Exemple #7
0
    def __init__(self, pads):
        touch1 = TSTriple()
        touch2 = TSTriple()
        touch3 = TSTriple()
        touch4 = TSTriple()
        self.specials += touch1.get_tristate(pads.t1)
        self.specials += touch2.get_tristate(pads.t2)
        self.specials += touch3.get_tristate(pads.t3)
        self.specials += touch4.get_tristate(pads.t4)

        self.o = CSRStorage(size=4)
        self.oe = CSRStorage(size=4)
        self.i = CSRStatus(size=4)

        self.comb += [
            touch1.o.eq(self.o.storage[0]),
            touch2.o.eq(self.o.storage[1]),
            touch3.o.eq(self.o.storage[2]),
            touch4.o.eq(self.o.storage[3]),
            touch1.oe.eq(self.oe.storage[0]),
            touch2.oe.eq(self.oe.storage[1]),
            touch3.oe.eq(self.oe.storage[2]),
            touch4.oe.eq(self.oe.storage[3]),
            self.i.status.eq(Cat(touch1.i, touch2.i, touch3.i, touch4.i))
        ]
    def __init__(
            self,
            pwm_pad,
            pwm_dev=None,
            clk_period=1e9/16e6,
            resolution=1e3,
            max_width=2000e3,
            csr_width=8,
            alignment=2,
        ):
        if pwm_dev == None:
            self.submodules.pwm_dev = ppm.PWMinput(
                clk_period=clk_period,
                resolution=resolution,
                max_width=max_width,
            )
        else:
            self.pwm_dev = pwm_dev

        # assign an input pin to the PPM device
        self.comb += self.pwm_dev.pwm.eq(pwm_pad)

        self.width = CSRStatus(csr_width, name='width')

        pwm_size = self.pwm_dev.width.nbits
        if csr_width >= pwm_size:
            self.comb += self.width.status[:pwm_size].eq(self.pwm_dev.width)
        else:
            self.comb += self.width.status.eq(self.pwm_dev.width[alignment:csr_width+alignment])
    def __init__(self, sys_clk_freq, pins):
        self.value = CSRStatus(8)

        self.submodules.encoder = _RotaryEncoder(sys_clk_freq)

        self.comb += [self.encoder.i_a.eq(pins.a), self.encoder.i_b.eq(pins.b)]

        self.sync += self.value.status.eq(self.encoder.o_counter)
Exemple #10
0
    def add_csr(self):
        csr_helper(self, 'channel', self.channel, cdc=True)
        csr_helper(self, 'threshold', self.threshold, cdc=True)
        csr_helper(self, 'wait_pre', self.wait_pre, cdc=True)
        csr_helper(self, 'wait_acq', self.wait_acq, cdc=True)
        csr_helper(self, 'wait_post', self.wait_post, cdc=True)

        self.trig_count_csr = CSRStatus(32, name='trig_count')
        self.specials += MultiReg(self.trig_count, self.trig_count_csr.status)
Exemple #11
0
    def add_csrs(self):
        self._start = CSR()
        self._start.description = 'Write to the register starts the transfer (if ready=1)'
        self._ready = CSRStatus(
            description='Indicates that the transfer is not ongoing')
        self._count = CSRStorage(size=len(self.count),
                                 description='Desired number of DMA transfers')
        self._done = CSRStatus(size=len(self.done),
                               description='Number of completed DMA transfers')
        self._mem_mask = CSRStorage(
            size=len(self.mem_mask),
            description='DRAM address mask for DMA transfers')
        self._data_mask = CSRStorage(size=len(self.mem_mask),
                                     description='Pattern memory address mask')

        self.comb += [
            self.start.eq(self._start.re),
            self._ready.status.eq(self.ready),
            self.count.eq(self._count.storage),
            self._done.status.eq(self.done),
            self.mem_mask.eq(self._mem_mask.storage),
            self.data_mask.eq(self._data_mask.storage),
        ]
Exemple #12
0
    def __init__(self):
        SimSoC.__init__(self, cpu_type=None)
        self.platform.add_source("I2C_slave.v")

        # For making sure csr registers are read back correctly
        self.status_test = CSRStatus(8)
        self.comb += self.status_test.status.eq(0xDE)

        # For testing bit-banging I2C
        self.submodules.i2c_master = m = I2CMaster()
        self.add_csr('i2c_master')

        # Hardwire SDA line to High!!!
        self.comb += m.pads.sda.eq(1)
    def __init__(self, pads):
        self.alt_fields = ["csr_control"]

        pins = [0, 1, 5, 6, 9, 10, 11, 12, 13, 18, 19, 20, 21]
        nbits = len(pins)
        fields = []

        for n in pins:
            fields += [
                CSRField(f"io{n}",
                         1,
                         n,
                         description=f"Control for I/O pin {n}"),
            ]

        self._oe = CSRStorage(nbits,
                              description="""GPIO Tristate(s) Control.
        Write ``1`` enable output driver""",
                              fields=fields)
        self._in = CSRStatus(nbits,
                             description="""GPIO Input(s) Status.
        Input value of IO pad as read by the FPGA""",
                             fields=fields)
        self._out = CSRStorage(nbits,
                               description="""GPIO Ouptut(s) Control.
        Value loaded into the output driver""",
                               fields=fields)

        # # #

        self._io = []
        for n, p in zip(pins, pads):
            m = IOPin(p)

            # Create a connection to the CSR
            alt_csr = io_pins()
            self.comb += [
                alt_csr.o.eq(self._out.storage[n]),
                alt_csr.oe.eq(self._oe.storage[n]),
                self._in.status.eq(alt_csr.i)
            ]
            m.add_alt(alt_csr)

            self.submodules += m
            self._io += [(n, m)]
    def __init__(
            self,
            ppm_pad,
            servo_pads=None,
            ppm_dev=None,
            channels=8,
            timeout=4000e3,
            clk_period=1e9/16e6,
            resolution=1e3,
            max_width=2000e3,
            csr_width=8,
            alignment=2,
        ):
        # instantiate the device if it isn't provided
        if ppm_dev == None:
            self.submodules.ppm_dev = ppm.PPMinput(
                channels=channels,
                timeout=timeout,
                clk_period=clk_period,
                resolution=resolution,
                max_width=max_width,
            )
        else:
            self.ppm_dev = ppm_dev
            channels = self.ppm_dev.channels

        # assign an input pin to the PPM device
        self.comb += self.ppm_dev.ppm.eq(ppm_pad)

        self.widths = []
        for chan in range(channels):
            tmp = CSRStatus(csr_width, name='channel_'+str(chan))
            self.widths.append(tmp)
            setattr(self, f"tmp{chan}", tmp)

        for chan in range(channels):
            ppm_size = self.ppm_dev.widths[chan].nbits
            if csr_width >= ppm_size:
                self.comb += self.widths[chan].status[:ppm_size].eq(self.ppm_dev.widths[chan])
            else:
                self.comb += self.widths[chan].status.eq(self.ppm_dev.widths[chan][alignment:csr_width+alignment])

            if servo_pads != None:
                assert(len(servo_pads) == channels)
                self.comb += servo_pads[chan].eq(self.ppm_dev.pwm[chan])
Exemple #15
0
    def add_csr(self, f_sys, p):
        ''' Wire up the config-registers to litex CSRs '''
        self.ddc.add_csr()
        self.pp.add_csr()
        self.pulse.add_csr()

        # sys clock domain
        n_ch = len(self.adcs)
        self.mags_sys = [Signal.like(self.mags_iir[0]) for i in range(n_ch)]
        self.phases_sys = [
            Signal.like(self.phases_iir[0]) for i in range(n_ch)
        ]

        # Clock domain crossing on self.strobe_
        self.submodules.cdc = BlindTransfer("sample", "sys",
                                            n_ch * (self.W_MAG + self.W_PHASE))

        # IIR controls
        self.iir = CSRStorage(len(self.iir_shift))
        self.specials += MultiReg(self.iir.storage, self.iir_shift, 'sample')
        self.comb += [
            self.cdc.data_i.eq(Cat(self.mags_iir + self.phases_iir)),
            self.cdc.i.eq(self.strobe_out),
            Cat(self.mags_sys + self.phases_sys).eq(self.cdc.data_o)
        ]

        # CSRs for peeking at phase / magnitude values
        for i, sig in enumerate(self.mags_sys + self.phases_sys):
            if i <= 3:
                n = 'mag{:d}'.format(i)
            else:
                n = 'phase{:d}'.format(i - 4)
            csr = CSRStatus(32, name=n)
            setattr(self, n, csr)
            self.specials += MultiReg(sig, csr.status)

        # Frequency counters for the ADC inputs
        for i, adc in enumerate(self.adcs):
            zc = ZeroCrosser(int(100e6))
            self.comb += zc.sig_in.eq(adc > 0)
            zc.add_csr()
            setattr(self.submodules, 'zc{}'.format(i), zc)
Exemple #16
0
    def __init__(self):
        self.wishbone = wishbone.Interface()

        self._start = CSRStorage(fields=[CSRField("start_burst", size=1, offset=0, pulse=True)])
        self._ready = CSRStatus(8)
        self._burst_size = CSRStorage(16)
        self._base = CSRStorage(32)
        self._offset = CSRStorage(32) 

        words_count = Signal(16)
        pass_count = Signal(5)
        
        self.wb_dma = wb_dma = WishboneDMAWriter(self.wishbone, endianness="big")
        self.submodules += wb_dma
        self.comb += [
            self.wb_dma.sink.address.eq((self._base.storage >> 2) + (self._offset.storage>>2) + words_count),
            self.wb_dma.sink.data.eq(pass_count),
        ]
                   
        fsm = FSM(reset_state="WAIT-FOR-TRIGGER")
        self.submodules += fsm

        
        fsm.act("WAIT-FOR-TRIGGER",
            self._ready.status.eq(1),
            NextValue(words_count, 0),
            If(self._start.fields.start_burst,
                NextState("WRITE-DATA"),
            )
        )

        fsm.act("WRITE-DATA",
            self.wb_dma.sink.valid.eq(1),

            If(self.wb_dma.sink.ready,    
                NextValue(words_count, words_count+1),
                If(words_count == (self._burst_size.storage-1),
                    NextState("WAIT-FOR-TRIGGER"),
                    NextValue(pass_count, pass_count+1)
                )
            )
        )
Exemple #17
0
 def __init__(self, width, depth, port, **kwargs):
     self.adc_data = adc_data = Signal(width)
     acq_start, acq_start_x = Signal(reset=0), Signal(reset=0)
     self._buf_full = CSRStatus(
         fields=[CSRField("acq_complete", size=1, offset=0)])
     self._acq_start = CSRStorage(
         fields=[CSRField("acq_start", size=1, offset=0, pulse=True)])
     w_addr = Signal(16, reset=0)
     self.comb += [
         self._buf_full.fields.acq_complete.eq(w_addr == depth),
         acq_start.eq(self._acq_start.fields.acq_start),
         port.adr.eq(w_addr),
         port.dat_w.eq(adc_data),
         port.we.eq(w_addr != depth)
     ]
     self.submodules.ps = PulseSynchronizer("sys", "sample")
     self.comb += [self.ps.i.eq(acq_start), acq_start_x.eq(self.ps.o)]
     self.sync.sample += [
         If(acq_start_x & (w_addr == depth),
            w_addr.eq(0)).Elif(w_addr != depth, w_addr.eq(w_addr + 1))
     ]
    def __init__(self, dma):
        address_width = len(dma.sink.address)

        self.enabled = CSRStorage(
            description="Used to start/stop the operation of the module")
        self.address1 = CSRStorage(address_width,
                                   description="First attacked address")
        self.address2 = CSRStorage(address_width,
                                   description="Second attacked address")
        self.count = CSRStatus(
            32,
            description="""This is the number of DMA accesses performed.
                                  When the module is enabled, the value can be freely read. When
                                  the module is disabled, the register is clear-on-write and has
                                  to be read before the next attack.""")

        counter = Signal.like(self.count.status)
        self.comb += self.count.status.eq(counter)
        self.sync += \
            If(self.enabled.storage,
                If(dma.sink.valid & dma.sink.ready,
                    counter.eq(counter + 1)
                )
            ).Elif(self.count.we,  # clear on read when not enabled
                counter.eq(0)
            )

        address = Signal(address_width)
        self.comb += Case(
            counter[0], {
                0: address.eq(self.address1.storage),
                1: address.eq(self.address2.storage),
            })

        self.comb += [
            dma.sink.address.eq(address),
            dma.sink.valid.eq(self.enabled.storage),
            dma.source.ready.eq(1),
        ]
Exemple #19
0
    def __init__(self, platform, pads):
        self.reset = Signal()

        mosi_pad = TSTriple()
        miso_pad = TSTriple()
        cs_n_pad = TSTriple()
        clk_pad = TSTriple()
        wp_pad = TSTriple()
        hold_pad = TSTriple()

        self.do = CSRStorage(size=6)
        self.oe = CSRStorage(size=6)
        self.di = CSRStatus(size=6)

        self.specials += mosi_pad.get_tristate(pads.mosi)
        self.specials += miso_pad.get_tristate(pads.miso)
        self.specials += cs_n_pad.get_tristate(pads.cs_n)
        self.specials += clk_pad.get_tristate(pads.clk)
        self.specials += wp_pad.get_tristate(pads.wp)
        self.specials += hold_pad.get_tristate(pads.hold)

        self.comb += [
            mosi_pad.oe.eq(self.oe.storage[0]),
            miso_pad.oe.eq(self.oe.storage[1]),
            wp_pad.oe.eq(self.oe.storage[2]),
            hold_pad.oe.eq(self.oe.storage[3]),
            clk_pad.oe.eq(self.oe.storage[4]),
            cs_n_pad.oe.eq(self.oe.storage[5]),
            mosi_pad.o.eq(self.do.storage[0]),
            miso_pad.o.eq(self.do.storage[1]),
            wp_pad.o.eq(self.do.storage[2]),
            hold_pad.o.eq(self.do.storage[3]),
            clk_pad.o.eq(self.do.storage[4]),
            cs_n_pad.o.eq(self.do.storage[5]),
            self.di.status.eq(
                Cat(mosi_pad.i, miso_pad.i, wp_pad.i, hold_pad.i, clk_pad.i,
                    cs_n_pad.i)),
        ]
    def __init__(self, pads):
        _fields_input = [
            CSRField("MODPRSL"),
            CSRField("INTL"),
        ]
        _fields_output = [
            CSRField("MODSELL", reset=1),
            CSRField("RESETL", reset=1),
            CSRField("LPMODE", reset=0),
        ]

        self.status = status = CSRStatus(fields=_fields_input,
                                         name="qsfp0_status")
        self.control = control = CSRStorage(fields=_fields_output,
                                            name="qsfp0_control")

        for _f in _fields_input:
            self.comb += getattr(status.fields,
                                 _f.name).eq(getattr(pads, _f.name))

        for _f in _fields_output:
            self.comb += getattr(pads,
                                 _f.name).eq(getattr(control.fields, _f.name))
    def __init__(self, pads, gpio_name):
        nbits = len(pads)
        fields = [CSRField(fld[0], description=fld[1]) for fld in gpio_name]
        self._oe = CSRStorage(nbits,
                              description="GPIO Tristate(s) Control.",
                              fields=fields)
        self._in = CSRStatus(nbits,
                             description="GPIO Input(s) Status.",
                             fields=fields)
        self._out = CSRStorage(nbits,
                               description="GPIO Output(s) Control.",
                               fields=fields)

        # # #

        _pads = Signal(nbits)
        self.comb += _pads.eq(pads)

        for i in range(nbits):
            t = TSTriple()
            self.specials += t.get_tristate(_pads[i])
            self.comb += t.oe.eq(self._oe.storage[i])
            self.comb += t.o.eq(self._out.storage[i])
            self.specials += MultiReg(t.i, self._in.status[i])
Exemple #22
0
    def __init__(self, pads):
        self.intro = ModuleDoc("""Fomu Touchpads

        Fomu has four single-ended exposed pads on its side.  These pads are designed
        to be connected to some captouch block, or driven in a resistive touch mode
        in order to get simple touchpad support.

        This block simply provides CPU-controlled GPIO support for this block.  It has
        three registers which control the In, Out, and Output Enable functionality of
        each of these pins.
        """)
        touch1 = TSTriple()
        touch2 = TSTriple()
        touch3 = TSTriple()
        touch4 = TSTriple()
        self.specials += touch1.get_tristate(pads.t1)
        self.specials += touch2.get_tristate(pads.t2)
        self.specials += touch3.get_tristate(pads.t3)
        self.specials += touch4.get_tristate(pads.t4)

        self.o = CSRStorage(4, description="Output values for pads 1-4")
        self.oe = CSRStorage(4,
                             description="Output enable control for pads 1-4")
        self.i = CSRStatus(4, description="Input value for pads 1-4")

        self.comb += [
            touch1.o.eq(self.o.storage[0]),
            touch2.o.eq(self.o.storage[1]),
            touch3.o.eq(self.o.storage[2]),
            touch4.o.eq(self.o.storage[3]),
            touch1.oe.eq(self.oe.storage[0]),
            touch2.oe.eq(self.oe.storage[1]),
            touch3.oe.eq(self.oe.storage[2]),
            touch4.oe.eq(self.oe.storage[3]),
            self.i.status.eq(Cat(touch1.i, touch2.i, touch3.i, touch4.i))
        ]
Exemple #23
0
    def __init__(self, platform, pads, size=16 * 1024 * 1024):
        self.size = size

        self.bus = bus = wishbone.Interface()

        self.reset = Signal()

        self.cfg1 = CSRStorage(fields=[
            CSRField(
                "bb_out", size=4, description="Output bits in bit-bang mode"),
            CSRField("bb_clk",
                     description="Serial clock line in bit-bang mode"),
            CSRField("bb_cs", description="Chip select line in bit-bang mode"),
        ])
        self.cfg2 = CSRStorage(fields=[
            CSRField("bb_oe",
                     size=4,
                     description="Output Enable bits in bit-bang mode"),
        ])
        self.cfg3 = CSRStorage(fields=[
            CSRField(
                "rlat", size=4, description="Read latency/dummy cycle count"),
            CSRField("crm", description="Continuous Read Mode enable bit"),
            CSRField("qspi", description="Quad-SPI enable bit"),
            CSRField("ddr", description="Double Data Rate enable bit"),
        ])
        self.cfg4 = CSRStorage(fields=[
            CSRField(
                "memio",
                offset=7,
                reset=1,
                description=
                "Enable memory-mapped mode (set to 0 to enable bit-bang mode)")
        ])

        self.stat1 = CSRStatus(fields=[
            CSRField(
                "bb_in", size=4, description="Input bits in bit-bang mode"),
        ])
        self.stat2 = CSRStatus(8, description="Reserved")
        self.stat3 = CSRStatus(8, description="Reserved")
        self.stat4 = CSRStatus(8, description="Reserved")

        cfg = Signal(32)
        cfg_we = Signal(4)
        cfg_out = Signal(32)
        self.comb += [
            cfg.eq(
                Cat(self.cfg1.storage, self.cfg2.storage, self.cfg3.storage,
                    self.cfg4.storage)),
            cfg_we.eq(
                Cat(self.cfg1.re, self.cfg2.re, self.cfg3.re, self.cfg4.re)),
            self.stat1.status.eq(cfg_out[0:4]),
            self.stat2.status.eq(0),
            self.stat3.status.eq(0),
            self.stat4.status.eq(0),
        ]

        reset = Signal()

        mosi_pad = TSTriple()
        miso_pad = TSTriple()
        cs_n_pad = TSTriple()
        clk_pad = TSTriple()
        wp_pad = TSTriple()
        hold_pad = TSTriple()

        clk = Signal()
        if hasattr(pads, "clk"):
            clk = pads.clk
            self.specials += clk_pad.get_tristate(clk)
            self.comb += clk_pad.oe.eq(~reset)
        else:
            self.specials += Instance("USRMCLK", i_USRMCLKI=clk, i_USRMCLKTS=0)

        self.specials += mosi_pad.get_tristate(pads.mosi)
        self.specials += miso_pad.get_tristate(pads.miso)
        self.specials += cs_n_pad.get_tristate(pads.cs_n)
        self.specials += wp_pad.get_tristate(pads.wp)
        self.specials += hold_pad.get_tristate(pads.hold)

        self.comb += [
            reset.eq(ResetSignal() | self.reset),
            cs_n_pad.oe.eq(~reset),
        ]

        flash_addr = Signal(24)
        # size/4 because data bus is 32 bits wide, -1 for base 0
        mem_bits = bits_for(int(size / 4) - 1)
        pad = Signal(2)
        self.comb += flash_addr.eq(Cat(pad, bus.adr[0:mem_bits - 1]))

        read_active = Signal()
        spi_ready = Signal()
        self.sync += [
            If(
                bus.stb & bus.cyc & ~read_active,
                read_active.eq(1),
                bus.ack.eq(0),
            ).Elif(
                read_active & spi_ready,
                read_active.eq(0),
                bus.ack.eq(1),
            ).Else(
                bus.ack.eq(0),
                read_active.eq(0),
            )
        ]

        o_rdata = Signal(32)
        self.comb += bus.dat_r.eq(o_rdata)

        self.specials += Instance(
            "spimemio",
            o_flash_io0_oe=mosi_pad.oe,
            o_flash_io1_oe=miso_pad.oe,
            o_flash_io2_oe=wp_pad.oe,
            o_flash_io3_oe=hold_pad.oe,
            o_flash_io0_do=mosi_pad.o,
            o_flash_io1_do=miso_pad.o,
            o_flash_io2_do=wp_pad.o,
            o_flash_io3_do=hold_pad.o,
            o_flash_csb=cs_n_pad.o,
            o_flash_clk=clk,
            i_flash_io0_di=mosi_pad.i,
            i_flash_io1_di=miso_pad.i,
            i_flash_io2_di=wp_pad.i,
            i_flash_io3_di=hold_pad.i,
            i_resetn=~reset,
            i_clk=ClockSignal(),
            i_valid=bus.stb & bus.cyc,
            o_ready=spi_ready,
            i_addr=flash_addr,
            o_rdata=o_rdata,
            i_cfgreg_we=cfg_we,
            i_cfgreg_di=cfg,
            o_cfgreg_do=cfg_out,
        )
        platform.add_source("rtl/spimemio.v")
Exemple #24
0
 def add_csr(self):
     self.f_ref_csr = CSRStatus(32, name='f_meas')
     self.specials += MultiReg(self.n_zc, self.f_ref_csr.status)
Exemple #25
0
    def __init__(self, model, models, seed):
        """Create a `Version` block.

        Arguments:

        model (int): The value of the :obj:`CSRStatus(model)` field.
        models: a list() of (value, "shortname", "description") tuples.
        """
        self.intro = ModuleDoc("""SoC Version Information

            This block contains various information about the state of the source code
            repository when this SoC was built.
            """)

        def makeint(i, base=10):
            try:
                return int(i, base=base)
            except:
                return 0

        def get_gitver():
            def decode_version(v):
                version = v.split(".")
                major = 0
                minor = 0
                rev = 0
                if len(version) >= 3:
                    rev = makeint(version[2])
                if len(version) >= 2:
                    minor = makeint(version[1])
                if len(version) >= 1:
                    major = makeint(version[0])
                return (major, minor, rev)

            git_rev_cmd = subprocess.Popen(
                ["git", "describe", "--tags", "--dirty=+"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)
            (git_stdout, _) = git_rev_cmd.communicate()
            if git_rev_cmd.wait() != 0:
                print('unable to get git version')
                return
            raw_git_rev = git_stdout.decode().strip()

            dirty = 0
            if raw_git_rev[-1] == "+":
                raw_git_rev = raw_git_rev[:-1]
                dirty = 1

            parts = raw_git_rev.split("-")
            major = 0
            minor = 0
            rev = 0
            gitrev = 0
            gitextra = 0

            if len(parts) >= 3:
                if parts[0].startswith("v"):
                    version = parts[0]
                    if version.startswith("v"):
                        version = parts[0][1:]
                    (major, minor, rev) = decode_version(version)
                gitextra = makeint(parts[1])
                if parts[2].startswith("g"):
                    gitrev = makeint(parts[2][1:], base=16)
            elif len(parts) >= 2:
                if parts[1].startswith("g"):
                    gitrev = makeint(parts[1][1:], base=16)
                version = parts[0]
                if version.startswith("v"):
                    version = parts[0][1:]
                (major, minor, rev) = decode_version(version)
            elif len(parts) >= 1:
                version = parts[0]
                if version.startswith("v"):
                    version = parts[0][1:]
                (major, minor, rev) = decode_version(version)

            return (major, minor, rev, gitrev, gitextra, dirty)

        (major, minor, rev, gitrev, gitextra, dirty) = get_gitver()

        self.major = CSRStatus(
            8,
            reset=major,
            description=
            "Major git tag version.  For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``."
            .format(major, minor, rev, major))
        self.minor = CSRStatus(
            8,
            reset=minor,
            description=
            "Minor git tag version.  For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``."
            .format(major, minor, rev, minor))
        self.revision = CSRStatus(
            8,
            reset=rev,
            description=
            "Revision git tag version.  For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``."
            .format(major, minor, rev, rev))
        self.gitrev = CSRStatus(
            32,
            reset=gitrev,
            description=
            "First 32-bits of the git revision.  This documentation was built from git rev ``{:08x}``, so this value is {}, which should be enough to check out the exact git version used to build this firmware."
            .format(gitrev, gitrev))
        self.gitextra = CSRStatus(
            10,
            reset=gitextra,
            description=
            "The number of additional commits beyond the git tag.  For example, if this value is ``1``, then the repository this was built from has one additional commit beyond the tag indicated in `MAJOR`, `MINOR`, and `REVISION`."
        )
        self.dirty = CSRStatus(fields=[
            CSRField(
                "dirty",
                reset=dirty,
                description=
                "Set to ``1`` if this device was built from a git repo with uncommitted modifications."
            )
        ])

        model_val = None
        for model_check in models:
            try:
                if int(model_check[0]) == int(model):
                    model_val = int(model_check[0])
            except:
                pass
            try:
                if model_check[1] == model:
                    model_val = int(model_check[0])
            except:
                pass
        if model_val is None:
            raise ValueError("Model not found in `models` list!")

        self.model = CSRStatus(fields=[
            CSRField(
                "model",
                reset=model_val,
                size=8,
                description=
                "Contains information on which model device this was built for.",
                values=models)
        ])
        self.seed = CSRStatus(
            32,
            reset=seed,
            description="32-bit seed used for the place-and-route.")

        self.comb += [
            self.major.status.eq(major),
            self.minor.status.eq(minor),
            self.revision.status.eq(rev),
            self.gitrev.status.eq(gitrev),
            self.gitextra.status.eq(gitextra),
            self.dirty.fields.dirty.eq(dirty),
            self.model.fields.model.eq(model_val),
            self.seed.status.eq(seed),
        ]
    def __init__(self,
                 pads,
                 dummy=15,
                 div=2,
                 with_bitbang=True,
                 endianness="big"):
        """
        Simple SPI flash.
        Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast
        Read). Only supports mode0 (cpol=0, cpha=0).
        """
        self.bus = bus = wishbone.Interface()
        spi_width = len(pads.dq)
        assert spi_width >= 2

        if with_bitbang:
            self.bitbang = CSRStorage(4)
            self.miso = CSRStatus()
            self.bitbang_en = CSRStorage()

        # # #

        cs_n = Signal(reset=1)
        clk = Signal()
        dq_oe = Signal()
        wbone_width = len(bus.dat_r)

        read_cmd_params = {
            4: (_format_cmd(_QIOFR, 4), 4 * 8),
            2: (_format_cmd(_DIOFR, 2), 2 * 8),
        }
        read_cmd, cmd_width = read_cmd_params[spi_width]
        addr_width = 24

        dq = TSTriple(spi_width)
        self.specials.dq = dq.get_tristate(pads.dq)

        sr = Signal(max(cmd_width, addr_width, wbone_width))
        if endianness == "big":
            self.comb += bus.dat_r.eq(sr)
        else:
            self.comb += bus.dat_r.eq(reverse_bytes(sr))

        hw_read_logic = [
            pads.clk.eq(clk),
            pads.cs_n.eq(cs_n),
            dq.o.eq(sr[-spi_width:]),
            dq.oe.eq(dq_oe)
        ]

        if with_bitbang:
            bitbang_logic = [
                pads.clk.eq(self.bitbang.storage[1]),
                pads.cs_n.eq(self.bitbang.storage[2]),

                # In Dual/Quad mode, no single data pin is consistently
                # an input or output thanks to dual/quad reads, so we need a bit
                # to swap direction of the pins. Aside from this additional bit,
                # bitbang mode is identical for Single/Dual/Quad; dq[0] is mosi
                # and dq[1] is miso, meaning remaining data pin values don't
                # appear in CSR registers.
                If(self.bitbang.storage[3], dq.oe.eq(0)).Else(dq.oe.eq(1)),
                If(
                    self.bitbang.
                    storage[1],  # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
                    self.miso.status.eq(dq.i[1])),
                dq.o.eq(
                    Cat(self.bitbang.storage[0], Replicate(1, spi_width - 1)))
            ]

            self.comb += [
                If(self.bitbang_en.storage, bitbang_logic).Else(hw_read_logic)
            ]

        else:
            self.comb += hw_read_logic

        if div < 2:
            raise ValueError(
                "Unsupported value \'{}\' for div parameter for SpiFlash core".
                format(div))
        else:
            i = Signal(max=div)
            dqi = Signal(spi_width)
            self.sync += [
                If(
                    i == div // 2 - 1,
                    clk.eq(1),
                    dqi.eq(dq.i),
                ),
                If(i == div - 1, i.eq(0), clk.eq(0),
                   sr.eq(Cat(dqi, sr[:-spi_width]))).Else(i.eq(i + 1), ),
            ]

        # spi is byte-addressed, prefix by zeros
        z = Replicate(0, log2_int(wbone_width // 8))

        seq = [
            (cmd_width // spi_width * div,
             [dq_oe.eq(1),
              cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]),
            (addr_width // spi_width * div,
             [sr[-addr_width:].eq(Cat(z, bus.adr))]),
            ((dummy + wbone_width // spi_width) * div, [dq_oe.eq(0)]),
            (1, [bus.ack.eq(1), cs_n.eq(1)]),
            (
                div,  # tSHSL!
                [bus.ack.eq(0)]),
            (0, []),
        ]

        # accumulate timeline deltas
        t, tseq = 0, []
        for dt, a in seq:
            tseq.append((t, a))
            t += dt

        self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq)
    def __init__(self,
                 pads,
                 dummy=15,
                 div=2,
                 with_bitbang=True,
                 endianness="big"):
        """
        Simple SPI flash.
        Supports 1-bit reads. Only supports mode0 (cpol=0, cpha=0).
        """
        self.bus = bus = wishbone.Interface()

        if with_bitbang:
            self.bitbang = CSRStorage(4)
            self.miso = CSRStatus()
            self.bitbang_en = CSRStorage()

        # # #

        if hasattr(pads, "wp"):
            self.comb += pads.wp.eq(1)

        if hasattr(pads, "hold"):
            self.comb += pads.hold.eq(1)

        cs_n = Signal(reset=1)
        clk = Signal()
        wbone_width = len(bus.dat_r)

        read_cmd = _FAST_READ
        cmd_width = 8
        addr_width = 24

        sr = Signal(max(cmd_width, addr_width, wbone_width))
        if endianness == "big":
            self.comb += bus.dat_r.eq(sr)
        else:
            self.comb += bus.dat_r.eq(reverse_bytes(sr))

        hw_read_logic = [
            pads.clk.eq(clk),
            pads.cs_n.eq(cs_n),
            pads.mosi.eq(sr[-1:])
        ]

        if with_bitbang:
            bitbang_logic = [
                pads.clk.eq(self.bitbang.storage[1]),
                pads.cs_n.eq(self.bitbang.storage[2]),
                If(
                    self.bitbang.
                    storage[1],  # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only.
                    self.miso.status.eq(pads.miso)),
                pads.mosi.eq(self.bitbang.storage[0])
            ]

            self.comb += [
                If(self.bitbang_en.storage, bitbang_logic).Else(hw_read_logic)
            ]

        else:
            self.comb += hw_read_logic

        if div < 2:
            raise ValueError(
                "Unsupported value \'{}\' for div parameter for SpiFlash core".
                format(div))
        else:
            i = Signal(max=div)
            miso = Signal()
            self.sync += [
                If(
                    i == div // 2 - 1,
                    clk.eq(1),
                    miso.eq(pads.miso),
                ),
                If(i == div - 1, i.eq(0), clk.eq(0),
                   sr.eq(Cat(miso, sr[:-1]))).Else(i.eq(i + 1), ),
            ]

        # spi is byte-addressed, prefix by zeros
        z = Replicate(0, log2_int(wbone_width // 8))

        seq = [
            (cmd_width * div, [cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]),
            (addr_width * div, [sr[-addr_width:].eq(Cat(z, bus.adr))]),
            ((dummy + wbone_width) * div, []),
            (1, [bus.ack.eq(1), cs_n.eq(1)]),
            (
                div,  # tSHSL!
                [bus.ack.eq(0)]),
            (0, []),
        ]

        # accumulate timeline deltas
        t, tseq = 0, []
        for dt, a in seq:
            tseq.append((t, a))
            t += dt

        self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq)
Exemple #28
0
    def __init__(self, pads):
        # All MIPI commands and data are 8-bits, with the exception of commands
        # to read and write memory to the LCD.
        # self.intro = ModuleDoc(title="LCDIF", body=""" """)
        self.cmd = CSRStorage(
            8, description="Send an 8-bit command to the LCD panel")
        self.data = CSRStorage(
            8, description="Send an 8-bit data byte to the LCD panel")
        self.ctrl = CSRStorage(
            6,
            fields=[
                CSRField("bl", description="Control the backlight state"),
                CSRField(
                    "reset",
                    reset=1,
                    description="Directly connected to the LCD ``reset`` line"
                ),
                CSRField(
                    "cs",
                    reset=1,
                    description="Directly connected to the LCD ``cs`` line"),
                CSRField(
                    "fbstart",
                    description="Switch to line renderer on the next VBL"),
                CSRField("fbena",
                         description="Enable input to the line renderer"),
                CSRField(
                    "read",
                    description="Set to ``1`` to read data from the device",
                    pulse=True),
            ])
        self.status = CSRStatus(2,
                                fields=[
                                    CSRField(
                                        "fmark",
                                        description="Start-of-frame marker"),
                                    CSRField("id", description="LCD ID pin"),
                                ])
        self.fb = CSRStorage(1,
                             fields=[CSRField("startcmd", description="???")])
        self.response = CSRStorage(
            8, description="Response data after reading from the device.")
        self.tpg = CSRStorage(1,
                              description="Test pattern generators",
                              fields=[
                                  CSRField(
                                      "bluething",
                                      description="Kinda pretty blue display"),
                                  CSRField("rainbow",
                                           description="RGB pattern test"),
                              ])

        self.submodules.wheel = Wheel()
        idx_counter = Signal(16)

        # Data pins
        d = TSTriple(len(pads.db))
        self.specials.d = d.get_tristate(pads.db)

        # D/CX (If 1, this is a data packet, otherwise it's a command packet)
        dcx = Signal()

        # RDX is 1 if this is a `read` packet (i.e. device-to-FPGA)
        rdx = Signal()

        # WRX is 1 if this is a `write` packet (i.e. device-to-FPGA)
        wrx = Signal()

        # TE is 1 on start-of-frame (?)
        te = Signal()

        self.comb += [
            self.status.fields.fmark.eq(pads.fmark),
            self.status.fields.id.eq(pads.id),
            pads.cs.eq(self.ctrl.fields.cs),
            pads.rst.eq(~self.ctrl.fields.reset),
            pads.blen.eq(self.ctrl.fields.bl),
            pads.rs.eq(dcx),
            pads.wr.eq(wrx),
            pads.rd.eq(rdx),
            te.eq(pads.fmark),
        ]

        self.submodules.fsm = fsm = FSM()

        fsm.act(
            "IDLE", wrx.eq(1), rdx.eq(1),
            If(
                self.cmd.re,
                wrx.eq(0),
                d.o.eq(self.cmd.storage),
                d.oe.eq(1),
                NextState("SEND_CMD"),
            ).Elif(
                self.data.re,
                wrx.eq(0),
                dcx.eq(1),
                d.o.eq(self.data.storage),
                d.oe.eq(1),
                NextState("SEND_DATA"),
            ).Elif(
                self.ctrl.fields.read,
                rdx.eq(0),
                dcx.eq(1),
                d.oe.eq(0),
                NextState("READ_DATA"),
            ).Elif(
                self.tpg.fields.rainbow,
                wrx.eq(0),
                d.o.eq(0x2c),
                d.oe.eq(1),
                NextValue(idx_counter, 1),
                NextState("START_SEND_RAINBOW"),
            ).Elif(
                self.tpg.fields.bluething,
                wrx.eq(0),
                d.o.eq(0x2c),
                d.oe.eq(1),
                NextValue(idx_counter, 1),
                NextState("START_SEND_BLUETHING"),
            ))

        fsm.act(
            "SEND_DATA",
            dcx.eq(1),
            wrx.eq(1),
            rdx.eq(1),
            d.o.eq(self.data.storage),
            d.oe.eq(1),
            NextState("IDLE"),
        )

        fsm.act(
            "SEND_CMD",
            dcx.eq(0),
            wrx.eq(1),
            rdx.eq(1),
            d.o.eq(self.cmd.storage),
            d.oe.eq(1),
            NextState("IDLE"),
        )

        fsm.act(
            "READ_DATA",
            dcx.eq(1),
            wrx.eq(1),
            rdx.eq(1),
            d.oe.eq(0),
            NextValue(self.response.storage, d.i),
            NextState("IDLE"),
        )

        offset_counter = Signal(16)
        x = Signal(9)
        y = Signal(9)

        fsm.act("START_SEND_RAINBOW", NextValue(x, 0), NextValue(y, 0),
                wrx.eq(1), rdx.eq(1), dcx.eq(1), d.o.eq(0x2c), d.oe.eq(1),
                NextState("SEND_RAINBOW"))

        fsm.act(
            "SEND_RAINBOW",
            dcx.eq(1),
            rdx.eq(1),
            wrx.eq(idx_counter[0]),
            NextValue(idx_counter, idx_counter + 1),

            # If the bottom bit is set, then increment X and/or Y.
            # We do it this way because of how the LCD encoding works.
            If(
                idx_counter[0],
                If(x < 479, NextValue(x, x + 1)).Else(
                    NextValue(x, 0),
                    If(
                        y < 319,
                        NextValue(y, y + 1),
                    ).Else(NextValue(y, 0), )),
            ),

            # Assign the actual pixel data
            If(y < 90, d.o.eq(Cat(x[0:5], Signal(6), Signal(5)))).Elif(
                y < 180, d.o.eq(Cat(Signal(5), x[0:6], Signal(6)))).Else(
                    d.o.eq(Cat(Signal(5), Signal(6), x[0:5]))),
            d.oe.eq(1),
            If(
                ~self.tpg.fields.rainbow,
                NextState("IDLE"),
            ),
        )

        fsm.act("START_SEND_BLUETHING", wrx.eq(1), rdx.eq(1), dcx.eq(1),
                d.o.eq(0x2c), d.oe.eq(1), NextState("SEND_BLUETHING"))

        fsm.act(
            "SEND_BLUETHING",
            dcx.eq(1),
            rdx.eq(1),
            wrx.eq(idx_counter[0]),
            self.wheel.pos.eq(idx_counter[6:] + offset_counter[15:]),
            If(idx_counter > 320 * 2, NextValue(idx_counter, 0)).Else(
                NextValue(idx_counter, idx_counter + 1 + pads.fmark), ),
            NextValue(offset_counter, offset_counter + 1),
            d.oe.eq(1),
            d.o.eq(Cat(self.wheel.r[0:6], self.wheel.g[0:6],
                       self.wheel.b[0:6])),
            If(
                ~self.tpg.fields.bluething,
                NextState("IDLE"),
            ),
        )
Exemple #29
0
    def __init__(self, platform, mem, minimal=False):

        self.intro = ModuleDoc("""FOMU Apple II+
            A virtual computer within a virtual computer inside a USB port.
            Instantiate 6502 processor with 1MHz core clock.
            Tie in to system memory as a wishbone master.
            Create virtual keyboard, video display and disk drive.
            """)

        addr = Signal(16)
        dout = Signal(8)
        din = Signal(8)
        wren = Signal()
        iosel = Signal()
        ior_addr = Signal(8)
        r_memsel = Signal()
        w_memsel = Signal()
        clk_en = Signal()  # Clock divider limits CPU activity
        active = Signal()  # CPU is active this cycle
        available = Signal()  # Key press ready for reading
        div1M = 4
        idlecount = Signal(div1M)  # Counter to slow CPU clock

        # Disk II Controller Registers
        disk_phase = Signal(4)
        disk_motor = Signal()
        disk_drive = Signal()
        #disk_write   = Signal()
        disk_reading = Signal()  # DOS trying to read sector
        disk_data_available = Signal()  # Data available for DOS to read
        disk_data_wanted = Signal()  # Data wanted by DOS
        disk_read = Signal()  # Data read by DOS so clear readable

        simulation = getenv("SIMULATION")
        synthesis = not simulation

        # Wishbone visible registers
        self.control = CSRStorage(fields=[
            CSRField(
                "Reset",
                reset=1 if synthesis else 0,  # auto-start in sim
                description="6502 Reset line - 1: Reset Asserted, 0: Running"),
            CSRField("RWROM", size=1, description="Allow writes to ROM"),
            #CSRField("Pause", description="Halt processor allowing stepping"),
            #CSRField("Step",  description="Single step 6502 one clock cycle"),
            #CSRField("NMI", size=1, description="Non-maskable interrupt"),
            #CSRField("IRQ", size=1, description="Maskable interrupt request"),
            #CSRField("RDY", size=1, description=""),
            CSRField(
                "Divisor",
                size=div1M,
                offset=8,
                reset=11 if synthesis else 0,  # Over-clock simulation
                description="Clock divider minius 1: 11 for 1MHz, 0 for 12MHz"
            ),
            #CSRField("DI", size=8, offset=16, description=""),
        ])
        self.keyboard = CSRStorage(8,
                                   write_from_dev=True,
                                   description="Keyboard input ($C000)")
        self.strobe = CSR(1)  #, description="Keyboard strobe ($C010)")
        self.screen = CSRStatus(
            fields=[
                CSRField("Character",
                         size=8,
                         description="Character written to screen"),
                CSRField("Valid", size=1, description="Character is valid"),
                CSRField(
                    "More",
                    size=1,
                    description="Additional characters are available to read"),
                #CSRField("Move", size=1,
                #    description="Character is not adjacent to previous"),
                CSRField("Repeat",
                         size=1,
                         offset=11,
                         description=
                         "Previous character repeated to current position"),
                CSRField("ScrollStart",
                         size=1,
                         offset=12,
                         description="Start of scroll region detected"),
                CSRField("ScrollEnd",
                         size=1,
                         offset=13,
                         description="End of scroll region"),
                CSRField(
                    "Horizontal",
                    size=6,
                    offset=16,
                    description="Location of current character in screen memory"
                ),
                CSRField(
                    "Vertical",
                    size=5,
                    offset=24,
                    description="Location of current character in screen memory"
                ),
            ],
            description="Video Display Output")
        self.diskctrl = CSRStatus(
            fields=[
                CSRField("Phase",
                         size=4,
                         description=
                         "Four phases of the track selection stepper motor"),
                CSRField("Motor", size=1, description="Drive is spinning"),
                CSRField(
                    "Drive",
                    size=1,
                    description="Drive select: drive 1 if clear, drive 2 if set"
                ),
                CSRField("Wanted",
                         size=1,
                         description="Drive is waiting for data"),
                CSRField("Pending",
                         size=1,
                         description="Drive has not yet read data written"),
                #CSRField("WriteMode", size=1,
                #    description="Drive is reading when clear, writing when set"),
            ],
            description="Disk drive control ($C0EX)")
        self.diskdata = CSRStorage(8, description="Disk drive data ($C0EC)")

        #self.bus=CSRStatus(32, fields=[
        #    CSRField("Addr", size=16, description="Address bus"),
        #    CSRField("Data", size=8, description="Data bus"),
        #    CSRField("WrEn", size=1, description="Write enable"),
        #    ], description="Address and data bus")
        #self.debug=CSRStatus(32, fields=[
        #    CSRField("PC", size=8,
        #        description="Program counter"),
        #    CSRField("A", size=8,
        #        description="Accumulator"),
        #    CSRField("X", size=8,
        #        description="X index register"),
        #    CSRField("Y", size=8,
        #        description="Y index register"),
        #    ], description="Address and data bus")

        if not minimal:
            # The minimal configuration provides all registers the host needs to
            # see so software can run unmodified. However, it does not implement
            # the 6502 to save gates. The video driver is also greatly reduced.

            # TODO eliminate wire [31:0] apple2_display_fifo_wrport_dat_r
            self.submodules.display_fifo = fifo.SyncFIFOBuffered(width=32,
                                                                 depth=256)

            self.comb += [
                mem.addr6502.eq(addr),
                mem.din6502.eq(dout),
                mem.wren6502.eq(wren),
                self.strobe.w.eq(available),
                #self.bus.fields.Addr.eq(addr),
                #self.bus.fields.Data.eq(dout),
                #self.bus.fields.WrEn.eq(wren),
                If(addr[8:16] == 0xC0, iosel.eq(1), w_memsel.eq(0),
                   mem.access6502.eq(0)).Else(iosel.eq(0), w_memsel.eq(1),
                                              mem.access6502.eq(active)),
                disk_read.eq(0),
                If(
                    r_memsel,
                    din.eq(mem.dout6502),
                ).Else(
                    # I/O Read Address Decoder (reading but not from memory)
                    If(
                        ior_addr[4:8] == 0x0,
                        din.eq(Cat(self.keyboard.storage[0:7], available)),
                    ),
                    # Disk II Controller Card in slot 6  (0x8 | 0x6)
                    # The only data to be read are locations C and D. Simplify the
                    # logic to only look at bit 0 of the address.
                    If(
                        ior_addr[4:8] == 0xE,
                        din[0:7].eq(self.diskdata.storage[0:7]),
                        # Write protect - TODO write is not supported
                        #If(ior_addr[0],
                        #    # Return high bit set - protected
                        #    din[7].eq(1)
                        #).Else(
                        disk_read.eq(1),
                        If(
                            disk_data_available | self.diskdata.re,
                            # Return byte given by host
                            din[7].eq(self.diskdata.storage[7]),
                        ).Else(
                            # Return high bit clear - data not available
                            din[7].eq(0), ),
                        #),
                    ),
                ),
                active.eq(clk_en & self.display_fifo.writable),
            ]

            self.sync += [
                # Slow clock to prototypical speed or as configured by user
                If(
                    idlecount == self.control.fields.Divisor,
                    idlecount.eq(0),
                    clk_en.eq(1),
                ).Else(
                    idlecount.eq(idlecount + 1),
                    clk_en.eq(0),
                ),
                # Read (DI) follows Write (AB/DO) by one clock cycle
                If(
                    active,
                    r_memsel.eq(w_memsel),
                    ior_addr.eq(addr[0:8]),
                ),
                # Keyboard key available when written by host
                If(
                    self.keyboard.re,
                    available.eq(1),
                ),
                If(
                    iosel,
                    # I/O Write Address Decoder
                    If(
                        addr[4:8] == 0x1,
                        # KBDSTRB
                        # Strobe cleared on read or write to KBDSTRB
                        # Any read or write to this address clears the pending key
                        available.eq(0),
                    ),
                ),
            ]

            self.specials += Instance(
                "cpu",
                i_clk=ClockSignal(),
                i_reset=ResetSignal() | self.control.storage[0],
                i_DI=din,
                # &(self.rand.we|self.cfg.storage[1])),
                o_AB=addr,  # .dat_w,
                o_DO=dout,  # .dat_w,
                o_WE=wren,
                i_IRQ=False,  # self.control.fields.IRQ,
                i_NMI=False,  # self.control.fields.NMI,  # seed.re,
                i_RDY=active,  # self.control.fields.RDY,
            )
            platform.add_source("rtl/verilog-6502/cpu.v")
            platform.add_source("rtl/verilog-6502/ALU.v")

            #===============================================================================
            #       Disk Drive - Emulate Disk II controller in slot 6
            #===============================================================================
            # The Disk II controller card has 16 addressable locations. Eight of these are
            # dedicated to moving the arm, two for motor control, two for drive selection
            # and four that handle read, write, and write protection detection.
            #===============================================================================

            self.comb += [
                self.diskctrl.fields.Phase.eq(disk_phase),
                self.diskctrl.fields.Motor.eq(disk_motor),
                self.diskctrl.fields.Drive.eq(disk_drive),
                #self.diskctrl.fields.WriteMode.eq(disk_write),
                self.diskctrl.fields.Wanted.eq(disk_data_wanted),
                self.diskctrl.fields.Pending.eq(disk_data_available),
            ]

            self.sync += [
                If(
                    self.diskdata.re,
                    disk_data_available.eq(1),
                ),
                # Set false again on read
                If(
                    active & disk_read,
                    disk_reading.eq(0),
                    If(
                        disk_data_available,
                        disk_data_wanted.eq(0),
                    ).Else(disk_data_wanted.eq(1), ),
                    disk_data_available.eq(0),
                ),
                If(
                    iosel,
                    # Disk II Controller Card in slot 6

                    # C0E0 PHASEOFF  Stepper motor phase 0 off.
                    # C0E1 PHASEON   Stepper motor phase 0 on.
                    # C0E2 PHASE1OFF Stepper motor phase 1 off.
                    # C0E3 PHASElON  Stepper motor phase 1 on.
                    # C0E4 PHASE2OFF Stepper motor phase 2 off.
                    # C0E5 PHASE2ON  Stepper notor phase 2 on.
                    # C0E6 PHASE3OFF Stepper motor phase 3 off.
                    # C0E7 PHASE3ON  Stepper motor phase 3 on.
                    # C0E8 MOTOROFF  Turn motor off.
                    # C0E9 MOTORON   Turn motor on.
                    # C0EA DRV0EN    Engage drive 1.
                    # C0EB DRV1EN    Engage drive 2.
                    # C0EC Q6L       Strobe Data Latch for I/O.
                    # C0ED Q6H       Load Data Latch.
                    # C0EE Q7H       Prepare latch for input (read from disk).
                    # C0EF Q7L       Prepare latch for output (write to disk).

                    # Q7L with Q6L = Read
                    # Q7L with Q6H = Sense Write Protect
                    # Q7H with Q6L = Write
                    # Q7H with Q6H = Load Write Latch
                    If(
                        addr[4:8] == 0xE,  # (8|6) 
                        # Addresses 0-7 simply update a bit in the status register
                        If(addr[0:4] == 0x0, disk_phase[0].eq(0)),
                        If(addr[0:4] == 0x1, disk_phase[0].eq(1)),
                        If(addr[0:4] == 0x2, disk_phase[1].eq(0)),
                        If(addr[0:4] == 0x3, disk_phase[1].eq(1)),
                        If(addr[0:4] == 0x4, disk_phase[2].eq(0)),
                        If(addr[0:4] == 0x5, disk_phase[2].eq(1)),
                        If(addr[0:4] == 0x6, disk_phase[3].eq(0)),
                        If(addr[0:4] == 0x7, disk_phase[3].eq(1)),
                        # Likewise, motor active and drive select update status
                        If(addr[0:4] == 0x8, disk_motor.eq(0)),
                        If(addr[0:4] == 0x9, disk_motor.eq(1)),
                        If(addr[0:4] == 0xA, disk_drive.eq(0)),
                        If(addr[0:4] == 0xB, disk_drive.eq(1)),
                        # Write is ignored and read must be delayed one clock tick
                        If(addr[0:4] == 0xC, disk_reading.eq(1)),
                        #If(addr[0:4]==0xD, disk_ior_wp.eq(1)),
                        #If(addr[0:4]==0xE, disk_write.eq(0)),
                        #If(addr[0:4]==0xF, disk_write.eq(1)),
                    ),
                ),
            ]

            #===============================================================================
            #       Video Output - Text Mode
            #===============================================================================
            # The Apple II screen memory contains 8 segments containing 3 rows each in a 128
            # byte block leaving 8 unused bytes in each of the 8 blocks To assist with
            # scroll detection, we convert memory addresses to screen coordinates.
            #===============================================================================

            # Video memory - Frame Buffer access shortcuts
            fbsel = Signal()
            fb_r = Signal()
            fb_w = Signal()

            # Conversion from memory address to X,Y screen coordinates
            segment = Signal(3)
            triple = Signal(7)
            third = Signal(2)
            horiz = Signal(6)
            vert = Signal(5)
            move = Signal()

            scroll_active = Signal()
            scroll_match = Signal()
            scroll_start = Signal()
            scroll_end = Signal()
            scroll_read = Signal()
            scroll_write_valid = Signal()
            scroll_next_col = Signal()
            scroll_next_row = Signal()
            scroll_sequential = Signal()
            read_horiz = Signal(6)
            read_vert = Signal(5)

            repeat_active = Signal()
            repeat_match = Signal()
            repeat_start = Signal()
            repeat_end = Signal()
            repeat_next_col = Signal()
            repeat_next_row = Signal()
            repeat_sequential = Signal()

            # Registers shared by scroll and repeat compression circuits
            #horiz_start = Signal(max=40)
            #vert_start = Signal(max=24)
            #horiz_end = Signal(max=40)
            #vert_end = Signal(max=24)
            prev_horiz = Signal(max=40)
            prev_vert = Signal(max=24)
            prev_char = Signal(8)
            prev_start = Signal()
            push_save = Signal()
            push_saving = Signal()

            fifo_out = Signal(32)

            self.comb += [
                # Detect access to frame memory: Address range 0x0400-0x7ff
                fbsel.eq((addr[10:15] == 0x1) & active),
                fb_r.eq(fbsel & ~wren),
                fb_w.eq(fbsel & wren),
                # Convert memory address to X,Y coordinates
                segment.eq(addr[7:10]),
                triple.eq(addr[0:7]),
                # TODO This generates reg - change to cause only wire
                If(
                    triple >= 80,
                    third.eq(2),
                    horiz.eq(addr[0:7] - 80),
                ).Else(
                    If(
                        triple >= 40,
                        third.eq(1),
                        horiz.eq(addr[0:7] - 40),
                    ).Else(
                        third.eq(0),
                        horiz.eq(addr[0:7]),
                    )),
                vert.eq(Cat(segment, third)),

                # TODO Detect scroll - frame buffer read immediately followed by
                # frame buffer write to character on previous line, directly above.
                # Scroll is Right to Left (asm: DEY at FC90 in Autostart ROM)
                scroll_match.eq((horiz == read_horiz)
                                & (vert + 1 == read_vert)),
                # & (din==read_char))  <== TODO Need to delay din by 1 cycle
                scroll_write_valid.eq(scroll_read & fb_w & scroll_match),
                scroll_start.eq(scroll_write_valid & ~scroll_active),
                # Scroll ends on any write that does not follow the required pattern
                scroll_end.eq(scroll_active & fb_w & ~scroll_write_valid),
                scroll_next_col.eq((horiz + 1 == prev_horiz)
                                   & (vert == prev_vert)),
                scroll_next_row.eq((horiz == 39) & (prev_horiz == 0)
                                   & (vert == prev_vert + 1)),
                scroll_sequential.eq(scroll_next_col | scroll_next_row),

                # Detect repeated charaters (spaces)
                # Clear is Left to Right (asm: INY at FCA2 in Autostart ROM)
                #               repeat_match.eq(fb_w & (dout==prev_char)),
                #               repeat_start.eq(repeat_match & repeat_sequential & ~repeat_active),
                #               repeat_end.eq(fb_w & repeat_active &
                #                   (~repeat_match |~repeat_sequential)),
                #               repeat_next_col.eq((horiz==prev_horiz+1) & (vert==prev_vert)),
                #               repeat_next_row.eq((horiz==0) & (prev_horiz==39) &
                #                   (vert==prev_vert+1)),
                #               repeat_sequential.eq(repeat_next_col | repeat_next_row),
                #               repeat_sequential.eq(repeat_next_col),  # This or the previous one

                # Place writes in the fifo
                self.display_fifo.din[8].eq(0),  # Valid is calculated
                self.display_fifo.din[9].eq(0),  # More is calculated
                #self.display_fifo.din[   10].eq(move),
                self.display_fifo.din[11].eq(repeat_end),
                If(
                    push_save,
                    self.display_fifo.din[0:8].eq(prev_char),
                    self.display_fifo.din[12].eq(prev_start),
                    self.display_fifo.din[13].eq(scroll_end),
                    #self.display_fifo.din[14:16].eq(0), # Reserved
                    self.display_fifo.din[16:22].eq(prev_horiz
                                                    ),  # 2 bits padding
                    self.display_fifo.din[24:29].eq(
                        prev_vert),  # 3 bits padding
                ).Elif(
                    push_saving,
                    # push_save will be valid on the next cycle - so push previous
                    self.display_fifo.din[0:8].eq(prev_char),
                    #self.display_fifo.din[    8].eq(0), # Valid is calculated
                    #self.display_fifo.din[    9].eq(0), # More is calculated
                    #self.display_fifo.din[   10].eq(move),
                    #self.display_fifo.din[   11].eq(repeat_end),
                    self.display_fifo.din[12].eq(scroll_start),
                    self.display_fifo.din[13].eq(scroll_end),
                    #self.display_fifo.din[14:16].eq(0), # Reserved
                    self.display_fifo.din[16:22].eq(prev_horiz
                                                    ),  # 2 bits padding
                    self.display_fifo.din[24:29].eq(
                        prev_vert),  # 3 bits padding
                ).Else(
                    #self.display_fifo.din.eq(Cat(dout, horiz, vert,
                    #    move, scroll_start, scroll_end, repeat_start, repeat_end)),
                    self.display_fifo.din[0:8].eq(dout),
                    #self.display_fifo.din[    8].eq(0), # Valid is calculated
                    #self.display_fifo.din[    9].eq(0), # More is calculated
                    #self.display_fifo.din[   10].eq(move),
                    #self.display_fifo.din[   11].eq(repeat_end),
                    self.display_fifo.din[12].eq(scroll_start),
                    self.display_fifo.din[13].eq(scroll_end),
                    #self.display_fifo.din[14:16].eq(0), # Reserved
                    self.display_fifo.din[16:22].eq(horiz),  # 2 bits padding
                    self.display_fifo.din[24:29].eq(vert),  # 3 bits padding
                ),
                self.display_fifo.we.eq(push_save | repeat_end | scroll_start
                                        | scroll_end
                                        | (fb_w & ~scroll_active
                                           & ~repeat_active & ~repeat_start)),
                push_saving.eq(((repeat_end & ~repeat_sequential) | scroll_end)
                               & ~push_save),

                # Retrieve characters from fifo
                self.display_fifo.re.eq(self.screen.we),
                self.screen.we.eq(self.display_fifo.re),
                self.screen.fields.Valid.eq(self.display_fifo.readable),
                self.screen.fields.More.eq(self.display_fifo.readable),
                self.screen.fields.Character.eq(fifo_out[0:8]),
                self.screen.fields.Horizontal.eq(fifo_out[16:22]),
                self.screen.fields.Vertical.eq(fifo_out[24:29]),
                #self.screen.fields.Move.eq(fifo_out[10]),
                self.screen.fields.Repeat.eq(fifo_out[11]),
                self.screen.fields.ScrollStart.eq(fifo_out[12]),
                self.screen.fields.ScrollEnd.eq(fifo_out[13]),
            ]

            self.sync += [
                fifo_out.eq(self.display_fifo.dout),

                # Scroll
                If(
                    scroll_start,
                    scroll_active.eq(1),
                    #horiz_start.eq(horiz),
                    #vert_start.eq(vert),
                    #horiz_end.eq(horiz),
                    #vert_end.eq(vert),
                ),
                If(
                    scroll_end,
                    push_save.eq(1),
                    scroll_active.eq(0),
                    # These happen on any write to the frame buffer
                    #prev_horiz.eq(horiz),
                    #prev_vert.eq(vert),
                    #prev_char.eq(dout),
                    prev_start.eq(scroll_start),
                ),
                If(
                    fb_r,
                    If((scroll_read & (scroll_sequential | ~scroll_active)) |
                       (repeat_active & repeat_sequential),
                       # A faithful 6502 model issues a read of the target
                       # address before the write cycle of an indirect store
                       # instruction.  Do nothing if we suspect the current read is
                       # actually part of a store instruction.
                       ).
                    Else(
                        scroll_read.eq(1),
                        read_horiz.eq(horiz),
                        read_vert.eq(vert),
                        # TODO read_char should be din on the next clock cycle
                        # read_char.eq(dout),
                    ),
                ),

                # Write to the frame buffer: remember the location and character
                # that generate the sequential and repeat signals which are used
                # by the scroll and clear functions. Also, update scroll signals.
                If(
                    fb_w,
                    scroll_read.eq(0),
                    prev_vert.eq(vert),
                    prev_horiz.eq(horiz),
                    prev_char.eq(dout),

                    # Repeat - Mostly needed for screen clearing operations but made
                    # generic to conserve bandwidth and allow any character to be
                    # repeated.
                    If(
                        repeat_match & repeat_sequential,
                        # Supress output, begin sequence if not started already
                        # Store location and character as this will be needed if the
                        # following write is not sequential
                        # Setting repeat is redundant if already active
                        repeat_active.eq(1),
                    ).Elif(
                        repeat_active & ~repeat_sequential,
                        # The cursor moved and we must terminate repeat mode
                        # indicating the last character in the sequence.  This is
                        # required whether or not the same character is present.
                        # Output saved location with Repeat flag set to mark end.
                        # Then output current signal set on next cycle
                        push_save.eq(1),
                        prev_start.eq(scroll_start),
                        repeat_active.eq(0),
                    ).Else(
                        # Push current character on stack either because:
                        # a. character is different breaking repeat
                        # b. character is different preventing repeat
                        # c. location is not sequential breaking repeat
                        # d. location is not sequential preventing repeat
                        # Cases a,c need to clear repeat. This is irrelevant for b,d
                        repeat_active.eq(0), ),
                ),
                # We can safely use the cycle after a write to frame memory as a
                # second push into the character fifo knowing that the only time the
                # 6502 has two consecutive write cycles is the JSR instruction which
                # writes the return address onto the stack, and the RMW instructions
                # INC, DEC, LSR, ASR, ROL, ROR which write the original and the new
                # values in two consecutive cycles. It is extremely poor programming
                # practice to keep the stack inside the frame buffer while clearing
                # or scrolling the screen so no special handling of these cases is
                # taken.
                If(
                    push_save,
                    # Auto-clear after one clock cycle
                    push_save.eq(0),
                ),
            ]
Exemple #30
0
    def __init__(self, pads, default=_default_edid):
        self._hpd_notif = CSRStatus()
        self._hpd_en = CSRStorage()
        mem_size = len(default)
        assert mem_size % 128 == 0
        self.specials.mem = Memory(8, mem_size, init=default)

        # # #

        # HPD
        if hasattr(pads, "hpd_notif"):
            if hasattr(getattr(pads, "hpd_notif"), "inverted"):
                hpd_notif_n = Signal()
                self.comb += hpd_notif_n.eq(~pads.hpd_notif)
                self.specials += MultiReg(hpd_notif_n, self._hpd_notif.status)
            else:
                self.specials += MultiReg(pads.hpd_notif,
                                          self._hpd_notif.status)
        else:
            self.comb += self._hpd_notif.status.eq(1)
        if hasattr(pads, "hpd_en"):
            self.comb += pads.hpd_en.eq(self._hpd_en.storage)

        # EDID
        scl_raw = Signal()
        sda_i = Signal()
        sda_raw = Signal()
        sda_drv = Signal()
        _sda_drv_reg = Signal()
        _sda_i_async = Signal()
        self.sync += _sda_drv_reg.eq(sda_drv)

        pad_scl = getattr(pads, "scl")
        if hasattr(pad_scl, "inverted"):
            self.specials += MultiReg(~pads.scl, scl_raw)
        else:
            self.specials += MultiReg(pads.scl, scl_raw)

        if hasattr(pads, "sda_drv"):
            pads_sda = getattr(pads, "sda")
            if hasattr(pads_sda, "inverted"):
                self.specials += MultiReg(~pads.sda, sda_raw)
            else:
                self.specials += MultiReg(pads.sda, sda_raw)
            self.comb += pads.sda_drv.eq(_sda_drv_reg)
        else:
            self.specials += [
                Tristate(pads.sda, 0, _sda_drv_reg, _sda_i_async),
                MultiReg(_sda_i_async, sda_raw)
            ]

        # for debug
        self.scl = scl_raw
        self.sda_i = sda_i
        self.sda_o = Signal()
        self.comb += self.sda_o.eq(~_sda_drv_reg)
        self.sda_oe = _sda_drv_reg

        scl_i = Signal()
        samp_count = Signal(6)
        samp_carry = Signal()
        self.sync += [
            Cat(samp_count, samp_carry).eq(samp_count + 1),
            If(samp_carry, scl_i.eq(scl_raw), sda_i.eq(sda_raw))
        ]

        scl_r = Signal()
        sda_r = Signal()
        scl_rising = Signal()
        sda_rising = Signal()
        sda_falling = Signal()
        self.sync += [scl_r.eq(scl_i), sda_r.eq(sda_i)]
        self.comb += [
            scl_rising.eq(scl_i & ~scl_r),
            sda_rising.eq(sda_i & ~sda_r),
            sda_falling.eq(~sda_i & sda_r)
        ]

        start = Signal()
        self.comb += start.eq(scl_i & sda_falling)

        din = Signal(8)
        counter = Signal(max=9)
        self.sync += [
            If(start, counter.eq(0)),
            If(
                scl_rising,
                If(counter == 8,
                   counter.eq(0)).Else(counter.eq(counter + 1),
                                       din.eq(Cat(sda_i, din[:7]))))
        ]

        self.din = din
        self.counter = counter

        is_read = Signal()
        update_is_read = Signal()
        self.sync += If(update_is_read, is_read.eq(din[0]))

        offset_counter = Signal(max=mem_size)
        oc_load = Signal()
        oc_inc = Signal()
        self.sync += \
            If(oc_load,
                offset_counter.eq(din)
            ).Elif(oc_inc,
                offset_counter.eq(offset_counter + 1)
            )

        rdport = self.mem.get_port()
        self.specials += rdport
        self.comb += rdport.adr.eq(offset_counter)
        data_bit = Signal()

        zero_drv = Signal()
        data_drv = Signal()
        self.comb += \
            If(zero_drv,
                sda_drv.eq(1)
            ).Elif(data_drv,
                sda_drv.eq(~data_bit)
            )

        data_drv_en = Signal()
        data_drv_stop = Signal()
        self.sync += \
            If(data_drv_en,
                data_drv.eq(1)
            ).Elif(data_drv_stop,
                data_drv.eq(0)
            )
        self.sync += \
            If(data_drv_en,
                chooser(rdport.dat_r, counter, data_bit, 8, reverse=True)
            )

        self.submodules.fsm = fsm = FSM()

        fsm.act("WAIT_START")
        fsm.act(
            "RCV_ADDRESS",
            If(
                counter == 8,
                If(din[1:] == 0x50, update_is_read.eq(1),
                   NextState("ACK_ADDRESS0")).Else(NextState("WAIT_START"))))
        fsm.act("ACK_ADDRESS0", If(~scl_i, NextState("ACK_ADDRESS1")))
        fsm.act("ACK_ADDRESS1", zero_drv.eq(1),
                If(scl_i, NextState("ACK_ADDRESS2")))
        fsm.act(
            "ACK_ADDRESS2", zero_drv.eq(1),
            If(~scl_i,
               If(is_read, NextState("READ")).Else(NextState("RCV_OFFSET"))))

        fsm.act("RCV_OFFSET",
                If(counter == 8, oc_load.eq(1), NextState("ACK_OFFSET0")))
        fsm.act("ACK_OFFSET0", If(~scl_i, NextState("ACK_OFFSET1")))
        fsm.act("ACK_OFFSET1", zero_drv.eq(1),
                If(scl_i, NextState("ACK_OFFSET2")))
        fsm.act("ACK_OFFSET2", zero_drv.eq(1),
                If(~scl_i, NextState("RCV_ADDRESS")))

        fsm.act(
            "READ",
            If(
                ~scl_i,
                If(counter == 8, data_drv_stop.eq(1),
                   NextState("ACK_READ")).Else(data_drv_en.eq(1))))
        fsm.act(
            "ACK_READ",
            If(scl_rising, oc_inc.eq(1),
               If(sda_i, NextState("WAIT_START")).Else(NextState("READ"))))

        for state in fsm.actions.keys():
            fsm.act(state, If(start, NextState("RCV_ADDRESS")))
            if hasattr(pads, "hpd_en"):
                fsm.act(state,
                        If(~self._hpd_en.storage, NextState("WAIT_START")))