예제 #1
0
파일: wishbone.py 프로젝트: shenki/liteeth
    def __init__(self, dw, nrxslots=2, ntxslots=2, endianness="big"):
        self.sink   = stream.Endpoint(eth_phy_description(dw))
        self.source = stream.Endpoint(eth_phy_description(dw))
        self.bus    = wishbone.Interface()

        # # #

        # storage in SRAM
        sram_depth = eth_mtu//(dw//8)
        self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots, endianness)
        self.comb += [
            self.sink.connect(self.sram.sink),
            self.sram.source.connect(self.source)
        ]

        # Wishbone interface
        wb_rx_sram_ifs = [wishbone.SRAM(self.sram.writer.mems[n], read_only=True)
            for n in range(nrxslots)]
        wb_tx_sram_ifs = [wishbone.SRAM(self.sram.reader.mems[n], read_only=False)
            for n in range(ntxslots)]
        wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs

        wb_slaves     = []
        decoderoffset = log2_int(sram_depth, need_pow2=False)
        decoderbits   = log2_int(len(wb_sram_ifs))
        for n, wb_sram_if in enumerate(wb_sram_ifs):
            def slave_filter(a, v=n):
                return a[decoderoffset:decoderoffset+decoderbits] == v
            wb_slaves.append((slave_filter, wb_sram_if.bus))
            self.submodules += wb_sram_if
        wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True)
        self.submodules += wb_con
예제 #2
0
 def __init__(self):
     self.submodules.txmem = wishbone.SRAM(1024)
     self.submodules.rxmem = wishbone.SRAM(1024)
     self.sysbus = wishbone.Interface()
     self.submodules.decoder = wishbone.Decoder(self.sysbus, [
         (mem_decoder(0x1000, 1024), self.txmem.bus),
         (mem_decoder(0x2000, 1024), self.rxmem.bus),
     ],
                                                register=True)
     self.bus = wishbone.Interface()
     self.submodules.dma = DMATest()
     self.submodules.arbiter = wishbone.Arbiter(
         [self.bus, self.dma.master_bus], self.sysbus)
예제 #3
0
    def __init__(self, platform, pads):
        self.submodules.ll = ClockDomainsRenamer("local")(SDLinkLayer(
            platform, pads))

        # Event interrupts and acknowledgment
        self.submodules.ev = EventManager()
        self.ev.read = EventSourcePulse()
        self.ev.write = EventSourcePulse()
        self.ev.finalize()
        self._connect_event(self.ev.read, self.ll.block_read_act,
                            self.ll.block_read_go)
        self._connect_event(self.ev.write, self.ll.block_write_act,
                            self.ll.block_write_done)

        # Wishbone access to SRAM buffers
        self.bus = wishbone.Interface()
        self.submodules.wb_rd_buffer = wishbone.SRAM(self.ll.rd_buffer,
                                                     read_only=False)
        self.submodules.wb_wr_buffer = wishbone.SRAM(self.ll.wr_buffer,
                                                     read_only=False)
        wb_slaves = [(lambda a: a[9] == 0, self.wb_rd_buffer.bus),
                     (lambda a: a[9] == 1, self.wb_wr_buffer.bus)]
        self.submodules.wb_decoder = wishbone.Decoder(self.bus,
                                                      wb_slaves,
                                                      register=True)

        # Local reset domain
        self._reset = CSRStorage()
        self.clock_domains.cd_local = ClockDomain()
        self.comb += self.cd_local.clk.eq(ClockSignal())
        self.comb += self.cd_local.rst.eq(ResetSignal() | self._reset.storage)

        # Current data operation
        self._read_act = CSRStatus()
        self._read_addr = CSRStatus(32)
        self._read_byteaddr = CSRStatus(32)
        self._read_num = CSRStatus(32)
        self._read_stop = CSRStatus()
        self._write_act = CSRStatus()
        self._write_addr = CSRStatus(32)
        self._write_byteaddr = CSRStatus(32)
        self._write_num = CSRStatus(32)
        self._preerase_num = CSRStatus(23)
        self._erase_start = CSRStatus(32)
        self._erase_end = CSRStatus(32)
        self.comb += [
            self._read_act.status.eq(self.ll.block_read_act),
            self._read_addr.status.eq(self.ll.block_read_addr),
            self._read_byteaddr.status.eq(self.ll.block_read_byteaddr),
            self._read_num.status.eq(self.ll.block_read_num),
            self._read_stop.status.eq(self.ll.block_read_stop),
            self._write_act.status.eq(self.ll.block_write_act),
            self._write_addr.status.eq(self.ll.block_write_addr),
            self._write_byteaddr.status.eq(self.ll.block_write_byteaddr),
            self._write_num.status.eq(self.ll.block_write_num),
            self._preerase_num.status.eq(self.ll.block_preerase_num),
            self._erase_start.status.eq(self.ll.block_erase_start),
            self._erase_end.status.eq(self.ll.block_erase_end),
        ]

        # Informational registers, not needed for data transfer
        self._info_bits = CSRStatus(16)
        self.comb += self._info_bits.status.eq(
            Cat(
                self.ll.mode_4bit,
                self.ll.mode_spi,
                self.ll.host_hc_support,
                Constant(False),  # Reserved bit 3
                Constant(False),  # Reserved bit 4
                Constant(False),  # Reserved bit 5
                Constant(False),  # Reserved bit 6
                Constant(False),  # Reserved bit 7
                self.ll.info_card_desel,
                self.ll.err_op_out_range,
                self.ll.err_unhandled_cmd,
                self.ll.err_cmd_crc,
            ))
        self._most_recent_cmd = CSRStatus(len(self.ll.cmd_in_cmd))
        self.comb += self._most_recent_cmd.status.eq(self.ll.cmd_in_cmd)
        self._card_status = CSRStatus(len(self.ll.card_status))
        self.comb += self._card_status.status.eq(self.ll.card_status)
예제 #4
0
    def __init__(self, pads):
        self.background = ModuleDoc(
            """MemLCD: Driver for the SHARP Memory LCD model LS032B7DD02

        The LS032B7DD02 is a 336x536 pixel black and white memory LCD, with a 200ppi dot pitch.
        Memory LCDs can be thought of as 'fast E-ink displays that consume a tiny bit of standby
        power', as seen by these properties:

        * Extremely low standby power (30uW typ hold mode)
        * No special bias circuitry required to maintain image in hold mode
        * 120 degree viewing angle, 1:35 contrast ratio
        * All control logic fabricated on-glass using TFT devices that are auditable with
          a common 40x power desktop microscope and a bright backlight source

        This last property in particular makes the memory LCD extremely well suited for situations
        where absolute visibility into the construction of a secure enclave is desired.

        The memory organization of the LS032B7DD02 is simple: 536 lines of pixel data 336 wide.
        Each pixel is 1 bit (the display is black and white), and is fed into the module from
        left to right as pixels 1 through 336, inclusive. Lines are enumerated from top to bottom,
        from 1 to 536, inclusive.

        The LCD can only receive serial data. The protocol is a synchronous serial interface with
        an active high chip select. All data words are transmitted LSB first. A line transfer is
        initiated by sending a 6-bit mode selection, a 10-bit row address, and the subsequent 336
        pixels, followed by 16 dummy bits which transfer the data from the LCD holding register to
        the display itself.

            .. wavedrom::
                :caption: Single line data transfer to memory LCD

                { "signal": [
                    { "name": "SCLK", "wave": "0.P.......|......|...|..l." },
                    { "name": "SCS", "wave": "01........|......|...|.0.." },
                    { "name": "SI", "wave": "0===x..===|======|==x|....", "data": ["M0", "M1", "M2", "R0", "R1", " ", "R8", "R9", "D0", "D1", "D2", " ", "D334", "D335"] },
                    { "node": ".....................a.b..."},
                ],
                  "edge": ['a<->b 16 cycles']
                }

        Alternatively, one can send successive lines without dropping SCS by substituting the 16 dummy
        bits at the end with a 6-bit don't care preamble (where the mode bits would have been), 10 bits
        of row address, and then the pixel data.

            .. wavedrom::
              :caption: Multiple line data transfer to memory LCD

              { "signal": [
                    { "name": "SCLK", "wave": "0.P.......|......|...|....|....." },
                    { "name": "SCS", "wave": "01........|......|...|....|....." },
                    { "name": "SI", "wave": "0===x..===|======|==x|.===|=====", "data": ["M0", "M1", "M2", "R0", "R1", " ", "R8", "R9", "D0", "D1", "D2", " ", "D334", "D335", "R0", "R1", " ", "R8", "R9", "D0", "D1"] },
                    { "node": ".....................a.b..."},
              ],
                "edge": ['a<->b 6 cycles']
              }

        The very last line in the multiple line data transfer must terminate with 16 dummy cycles.

        Mode bits M0-M2 have the following meaning:
           M0: Set to 1 when transferring data lines. Set to 0 for hold mode, see below.
           M1: VCOM inversion flag. Ignore when hardware strap pin EXTMODE is high. Betrusted
               sets EXTMODE high, so the VCOM inversion is handled by low-power aux hardware.
               When EXTMODE is low, software must explicitly manage the VCOM inversion flag such the
               flag polarity changes once every second "as much as possible".
           M2: Normally set to 0. Set to 1 for all clear (see below)

        Data bit polarity:
           1 = White
           0 = Black

        For 'Hold mode' and 'All clear', a total of 16 cycles are sent, the first three being
        the mode bit and the last 13 being dummy cycles.

            .. wavedrom::
              :caption: Hold and all clear timings

              { "signal": [
                    { "name": "SCLK", "wave": "0.P...|.l" },
                    { "name": "SCS", "wave": "01....|0." },
                    { "name": "SI", "wave": "0===x...", "data": ["M0", "M1", "M2", "R0", "R1", " ", "R8", "R9", "D0", "D1", "D2", " ", "D334", "D335", "R0", "R1", " ", "R8", "R9", "D0", "D1"] },
                    { "node": ".....a.b..."},
              ],
                "edge": ['a<->b 13 cycles']
            }

        All signals are 3.0V compatible, 5V tolerant (VIH is 2.7V-VDD). The display itself requires
        a single 5V power supply (4.8-5.5V typ 5.8V abs max). In hold mode, typical power is 30uW, max 330uW;
        with data updating at 1Hz, power is 250uW, max 750uW (SCLK=1MHz, EXTCOMMIN=1Hz).

        * The maximum clock frequency for SCLK is 2MHz (typ 1MHz).
        * EXTCOMMIN frequency is 1-10Hz, 1Hz typ
        * EXTCOMMIN minimum high duration is 1us
        * All rise/fall times must be less than 50ns
        * SCS setup time is 3us, hold time is 1us. Minimum low duration is 1us,
          minimum high is 188us for a data update, 12 us for a hold mode operation.
        * SI setup time is 120ns, 190ns hold time.
        * Operating temperature is -20 to 70C, storage temperature -30 to 80C.
        """)

        self.interface = ModuleDoc("""Wishbone interface for MemLCD

        MemLCD maintains a local framebuffer for the LCD. The CPU can read and write
        to the frame buffer to update pixel data, and then request a screen update to
        commit the frame buffer to the LCD.

        Only full lines can be updated on a memory LCD; partial updates are not possible.
        In order to optimize the update process, MemLCD maintains a "dirty bit" associated
        with each line. Only lines with modified pixels are written to the screen after an
        update request.

        A line is 336 bits wide. When padded to 32-bit words, this yields a line width of
        44 bytes (0x2C, or 352 bits). In order to simplify math, the frame buffer rounds
        the line width up to the nearest power of two, or 64 bytes.

        The unused bits can be used as a "hint" to the MemLCD block as to which lines
        require updating. If the unused bits have any value other than 0, the MemLCD block
        will update those lines when an "UpdateDirty" command is triggered. It is up
        to the CPU to set and clear the dirty bits, they are not automatically cleared
        by the block upon update. Typically the clearing of the bits would be handled
        during the update-finished interrupt handling routine. If the dirty bits are
        not used, an "UpdateAll" command can be invoked, which will update every
        line of the LCD regardless of the contents of the dirty bits.

        The total depth of the memory is thus 44 bytes * 536 lines = 23,584 bytes or
        5,896 words.

        Pixels are stored with the left-most pixel in the MSB of each 32-bit word, with
        the left-most pixels occupying the lowest address in the line.

        Lines are stored with the bottom line of the screen at the lowest address.

        These parameters are chosen so that a 1-bit BMP file can be copied into the frame
        buffer and it will render directly to the screen with no further transformations
        required.

        The CPU is responsible for not writing data to the LCD while it is updating. Concurrent
        writes to the LCD during updates can lead to unpredictable behavior.
        """)
        data_width = 32
        width = 336
        height = 536
        bytes_per_line = 44

        self.fb_depth = fb_depth = height * bytes_per_line // (data_width // 8)
        pixdata = Signal(32)
        pixadr_rd = Signal(max=fb_depth)

        # 1 is white, which is the "off" state
        fb_init = [0xffffffff] * int(fb_depth)
        for i in range(fb_depth // 11):
            fb_init[i * 11 + 10] = 0xffff
        mem = Memory(
            32, fb_depth, init=fb_init
        )  # may need to round up to 8192 if a power of 2 is required by migen
        # read port for pixel data out
        self.specials.rdport = mem.get_port(
            write_capable=False,
            mode=READ_FIRST)  # READ_FIRST allows BRAM to be used
        self.comb += self.rdport.adr.eq(pixadr_rd)
        self.comb += pixdata.eq(self.rdport.dat_r)
        # implementation note: vivado will complain about being unable to merge an output register, leading to
        # non-optimal timing, but a check of the timing path shows that at 100MHz there is about 4-5ns of setup margin,
        # so the merge is unnecessary in this case. Ergo, prefer comb over sync to reduce latency.

        # memory-mapped write port to wishbone bus
        self.bus = wishbone.Interface()
        self.submodules.wb_sram_if = wishbone.SRAM(mem, read_only=False)
        decoder_offset = log2_int(fb_depth, need_pow2=False)

        def slave_filter(a):
            return a[decoder_offset:32 -
                     decoder_offset] == 0  # no aliasing in the block

        self.submodules.wb_con = wishbone.Decoder(
            self.bus, [(slave_filter, self.wb_sram_if.bus)], register=True)

        self.command = CSRStorage(
            2,
            fields=[
                CSRField(
                    "UpdateDirty",
                    description="Write a ``1`` to flush dirty lines to the LCD",
                    pulse=True),
                CSRField(
                    "UpdateAll",
                    description="Update full screen regardless of tag state",
                    pulse=True),
            ])

        self.busy = CSRStatus(
            1,
            name="Busy",
            description=
            """A ``1`` indicates that the block is currently updating the LCD"""
        )

        self.prescaler = CSRStorage(8,
                                    reset=99,
                                    name="prescaler",
                                    description="""
        Prescaler value. LCD clock is module (clock / (prescaler+1)). Reset value: 99, so
        for a default sysclk of 100MHz this yields an LCD SCLK of 1MHz""")

        self.submodules.ev = EventManager()
        self.ev.done = EventSourceProcess()
        self.ev.finalize()
        self.comb += self.ev.done.trigger.eq(
            self.busy.status)  # Fire an interupt when busy drops

        self.sclk = sclk = getattr(pads, "sclk")
        self.scs = scs = getattr(pads, "scs")
        self.si = si = getattr(pads, "si")
        self.sendline = sendline = Signal()
        self.linedone = linedone = Signal()
        updateall = Signal()
        fetch_dirty = Signal()
        update_line = Signal(
            max=height
        )  # Keep track of both line and address to avoid instantiating a multiplier
        update_addr = Signal(max=height * bytes_per_line)

        fsm_up = FSM(reset_state="IDLE")
        self.submodules += fsm_up

        fsm_up.act(
            "IDLE",
            If(
                self.command.fields.UpdateDirty
                | self.command.fields.UpdateAll,
                NextValue(self.busy.status, 1), NextValue(fetch_dirty, 1),
                If(self.command.fields.UpdateAll,
                   NextValue(updateall, 1)).Else(NextValue(updateall, 0)),
                NextState("START")).Else(NextValue(self.busy.status, 0)))
        fsm_up.act(
            "START",
            NextValue(update_line, height),
            NextValue(
                update_addr, (height - 1) * bytes_per_line
            ),  # Represents the byte address of the beginning of the last line
            NextState("FETCHDIRTY"))
        fsm_up.act(
            "FETCHDIRTY",  # Wait one cycle delay for the pixel data to be retrieved before evaluating it
            NextState("CHECKDIRTY"))
        fsm_up.act(
            "CHECKDIRTY",
            If(update_line == 0, NextState("IDLE")).Else(
                If(
                    (pixdata[16:] != 0) | updateall,
                    NextState("DIRTYLINE"),
                ).Else(NextValue(update_line, update_line - 1),
                       NextValue(update_addr, update_addr - bytes_per_line),
                       NextState("FETCHDIRTY"))))
        fsm_up.act("DIRTYLINE", NextValue(fetch_dirty, 0), sendline.eq(1),
                   NextState("WAITDONE"))
        fsm_up.act(
            "WAITDONE",
            If(linedone, NextValue(fetch_dirty, 1),
               NextValue(update_line, update_line - 1),
               NextValue(update_addr, update_addr - bytes_per_line),
               NextState("FETCHDIRTY")))

        modeshift = Signal(16)
        mode = Signal(6)
        pixshift = Signal(32)
        pixcount = Signal(max=width)
        bitreq = Signal()
        bitack = Signal()
        self.comb += mode.eq(
            1
        )  # Always in line write mode, not clearing, no vcom management necessary
        fsm_phy = FSM(reset_state="IDLE")
        self.submodules += fsm_phy
        # Update_addr units is in bytes. [2:] turns bytes to words
        # pixcount units are in pixels. [3:] turns pixels to bytes
        self.comb += [
            If(fetch_dirty, pixadr_rd.eq(
                (update_addr + bytes_per_line - 4)[2:])).Else(
                    pixadr_rd.eq((update_addr + pixcount[3:])[2:]))
        ]
        scs_cnt = Signal(max=200)
        fsm_phy.act(
            "IDLE",
            NextValue(si, 0),
            NextValue(linedone, 0),
            If(
                sendline,
                NextValue(scs, 1),
                NextValue(scs_cnt, 200),  # 2 us setup
                NextValue(pixcount, 16),
                NextValue(modeshift, Cat(mode, update_line)),
                NextState("SCS_SETUP")).Else(NextValue(scs, 0)))
        fsm_phy.act(
            "SCS_SETUP",
            If(scs_cnt > 0,
               NextValue(scs_cnt, scs_cnt - 1)).Else(NextState("MODELINE")))
        fsm_phy.act(
            "MODELINE",
            If(pixcount > 0, NextValue(modeshift, modeshift[1:]),
               NextValue(si, modeshift[0]), NextValue(pixcount, pixcount - 1),
               bitreq.eq(1),
               NextState("MODELINEWAIT")).Else(NextValue(pixcount, 1),
                                               NextValue(pixshift, pixdata),
                                               NextState("DATA")))
        fsm_phy.act("MODELINEWAIT", If(bitack, NextState("MODELINE")))
        fsm_phy.act(
            "DATA",
            If(
                pixcount < width + 17,
                If(
                    pixcount[0:5] == 0,
                    NextValue(pixshift, pixdata),
                ).Else(NextValue(pixshift, pixshift[1:]), ), NextValue(scs, 1),
                NextValue(si, pixshift[0]), NextValue(pixcount, pixcount + 1),
                bitreq.eq(1), NextState("DATAWAIT")).Else(
                    NextValue(si, 0),
                    NextValue(scs_cnt, 100),  # 1 us hold
                    NextState("SCS_HOLD")))
        fsm_phy.act(
            "SCS_HOLD",
            If(scs_cnt > 0, NextValue(scs_cnt, scs_cnt - 1)).Else(
                NextValue(scs, 0),
                NextValue(scs_cnt, 100),  # 1us minimum low time
                NextState("SCS_LOW")))
        fsm_phy.act(
            "SCS_LOW",
            If(scs_cnt > 0,
               NextValue(scs_cnt, scs_cnt - 1)).Else(NextValue(linedone, 1),
                                                     NextState("IDLE")))
        fsm_phy.act("DATAWAIT", If(bitack, NextState("DATA")))

        # This handles clock division
        fsm_bit = FSM(reset_state="IDLE")
        self.submodules += fsm_bit
        clkcnt = Signal(8)
        fsm_bit.act("IDLE", NextValue(sclk, 0),
                    NextValue(clkcnt, self.prescaler.storage),
                    If(bitreq, NextState("SCLK_LO")))
        fsm_bit.act(
            "SCLK_LO", NextValue(clkcnt, clkcnt - 1),
            If(clkcnt < self.prescaler.storage[1:], NextValue(sclk, 1),
               NextState("SCLK_HI")))
        fsm_bit.act(
            "SCLK_HI", NextValue(clkcnt, clkcnt - 1),
            If(clkcnt == 0, NextValue(sclk, 0), NextState("IDLE"),
               bitack.eq(1)))
예제 #5
0
    def __init__(self,
                 tx_clk,
                 tx_data,
                 rx_clk,
                 rx_data,
                 data_width=20,
                 mem_depth=256):
        assert (data_width == 20)

        self.clock_domains.cd_tx = ClockDomain(reset_less=True)
        self.comb += self.cd_tx.clk.eq(tx_clk)
        self.clock_domains.cd_rx = ClockDomain(reset_less=True)
        self.comb += self.cd_rx.clk.eq(rx_clk)

        self.trigger = CSRStorage()
        trigger = Signal()
        self.specials += MultiReg(self.trigger.storage, trigger, "tx")

        sample_counter = Signal(max=mem_depth)
        write_enable = Signal()
        self.tx = Signal()

        fsm = ClockDomainsRenamer("rx")(FSM(reset_state="IDLE"))
        self.submodules += fsm

        fsm.act("IDLE", write_enable.eq(0), NextValue(sample_counter, 0),
                If(trigger, NextState("MEASURING")))
        fsm.act("MEASURING", write_enable.eq(1),
                NextValue(sample_counter, sample_counter + 1),
                If(sample_counter == mem_depth - 1, NextState("DONE")))
        fsm.act("DONE", write_enable.eq(0), NextValue(sample_counter, 0),
                If(trigger == 0, NextState("IDLE")))

        self.specials += MultiReg(~fsm.ongoing("MEASURING"), self.tx, "tx")
        self.comb += If(self.tx, tx_data.eq(0xfffff)).Else(tx_data.eq(0x00000))

        mem = Memory(data_width, mem_depth)
        self.specials.port = mem.get_port(write_capable=True,
                                          clock_domain="rx")

        # connect the write port to local logic
        self.comb += [
            self.port.adr.eq(sample_counter),
            self.port.dat_w.eq(rx_data),
            self.port.we.eq(write_enable)
        ]

        # attach a wishbone interface to the Memory() object, with a read-only port
        self.submodules.wb_sram_if = wishbone.SRAM(mem, read_only=True)

        # get the wishbone Interface accessor object
        self.bus = wishbone.Interface()

        # build an address filter object. This is a migen expression returns 1
        # when the address "a" matches the RAM address space. Useful for precisely
        # targeting the RAM relative to other wishbone-connected objects within
        # the logic, e.g. other RAM blocks or registers.
        #
        # This filter means the RAM object will occupy its entire own CSR block,
        # with aliased copies of the RAM filling the entire block
        def slave_filter(a):
            return 1

        # This filter constrains the RAM to just its own address space
        decoder_offset = log2_int(mem_depth, need_pow2=False)

        def slave_filter_noalias(a):
            return a[decoder_offset:32 - decoder_offset] == 0

        # connect the Wishbone bus to the Memory wishbone port, using the address filter
        # to help decode the address.
        # The decdoder address filter as a list with entries as follows:
        #   (filter_function, wishbone_interface.bus)
        # This is useful if you need to attach multiple local logic memories into the
        # same wishbone address bank.
        wb_con = wishbone.Decoder(self.bus,
                                  [(slave_filter, self.wb_sram_if.bus)],
                                  register=True)
        # add the decoder as a submodule so it gets pulled into the finalize sweep
        self.submodules += wb_con

        self.analyzer_signals = [
            self.tx,
            self.port.adr,
            self.port.dat_w,
            self.port.we,
        ]