Exemplo n.º 1
0
    def __init__(self, N_CLOCKS):
        '''
        a simple frequency counter, detecting zero crossings
        N_CLOCKS = integration time
        '''
        self.n_zc = Signal(32)  # Number of zero crossings
        self.sig_in = Signal()  # Input signal under test

        # ADC zero crossing frequency counter
        sig_in_ = Signal()
        f_accu = Signal.like(self.n_zc)
        _n_zc_sample = Signal.like(self.n_zc)
        strobe = Signal()
        meas_time = Signal.like(self.n_zc, reset=N_CLOCKS)
        self.sync.sample += [
            strobe.eq(0),
            sig_in_.eq(self.sig_in),
            If(meas_time == 0, meas_time.eq(N_CLOCKS), _n_zc_sample.eq(f_accu),
               f_accu.eq(0), strobe.eq(1)).Else(
                   # On positive wavefrom zero crossing
                   If(~sig_in_ & self.sig_in, f_accu.eq(f_accu + 1)),
                   meas_time.eq(meas_time - 1))
        ]
        self.submodules.cdc = BlindTransfer("sample", "sys", len(self.n_zc))
        self.comb += [
            self.cdc.data_i.eq(_n_zc_sample),
            self.n_zc.eq(self.cdc.data_o),
            self.cdc.i.eq(strobe)
        ]
Exemplo n.º 2
0
    def __init__(self, rt_packet):
        self.reset = CSRStorage()
        self.set_time = CSR()
        self.protocol_error = CSR(4)
        self.command_missed_cmd = CSRStatus(2)
        self.command_missed_chan_sel = CSRStatus(24)
        self.buffer_space_timeout_dest = CSRStatus(8)

        self.specials += MultiReg(self.reset.storage, rt_packet.reset, "rtio")

        set_time_stb = Signal()
        set_time_ack = Signal()
        self.submodules += CrossDomainRequest("rtio", set_time_stb,
                                              set_time_ack, None,
                                              rt_packet.set_time_stb,
                                              rt_packet.set_time_ack, None)
        self.sync += [
            If(set_time_ack, set_time_stb.eq(0)),
            If(self.set_time.re, set_time_stb.eq(1))
        ]
        self.comb += self.set_time.w.eq(set_time_stb)

        errors = [(rt_packet.err_unknown_packet_type, "rtio_rx", None, None),
                  (rt_packet.err_packet_truncated, "rtio_rx", None, None),
                  (rt_packet.err_command_missed, "rtio",
                   Cat(rt_packet.command_missed_cmd,
                       rt_packet.command_missed_chan_sel),
                   Cat(self.command_missed_cmd.status,
                       self.command_missed_chan_sel.status)),
                  (rt_packet.err_buffer_space_timeout, "rtio",
                   rt_packet.buffer_space_destination,
                   self.buffer_space_timeout_dest.status)]

        for n, (err_i, err_cd, din, dout) in enumerate(errors):
            if din is not None:
                data_width = len(din)
            else:
                data_width = 0

            xfer = BlindTransfer(err_cd, "sys", data_width=data_width)
            self.submodules += xfer

            self.comb += xfer.i.eq(err_i)

            err_pending = Signal()
            self.sync += [
                If(self.protocol_error.re & self.protocol_error.r[n],
                   err_pending.eq(0)),
                If(xfer.o, err_pending.eq(1))
            ]
            self.comb += self.protocol_error.w[n].eq(err_pending)

            if din is not None:
                self.comb += xfer.data_i.eq(din)
                self.sync += If(xfer.o & ~err_pending, dout.eq(xfer.data_o))
Exemplo n.º 3
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)
Exemplo n.º 4
0
    def __init__(self, pads):
        self.gpio_enable = CSRStorage(reset=1)
        self.gpio_in = CSRStatus(2)
        self.gpio_out = CSRStorage(2)
        self.gpio_oe = CSRStorage(2)

        self.i2c_divider = CSRStorage(16)
        self.i2c_address = CSRStorage(7)
        self.errors = CSR(2)

        # in helper clock domain
        self.adpll = Signal(24)
        self.adpll_stb = Signal()

        # # #

        programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer())
        self.submodules += programmer

        self.i2c_divider.storage.attr.add("no_retiming")
        self.i2c_address.storage.attr.add("no_retiming")
        self.specials += [
            MultiReg(self.i2c_divider.storage, programmer.i2c_divider,
                     "helper"),
            MultiReg(self.i2c_address.storage, programmer.i2c_address,
                     "helper")
        ]
        self.comb += [
            programmer.adpll.eq(self.adpll),
            programmer.adpll_stb.eq(self.adpll_stb)
        ]

        self.gpio_enable.storage.attr.add("no_retiming")
        self.gpio_out.storage.attr.add("no_retiming")
        self.gpio_oe.storage.attr.add("no_retiming")

        # SCL GPIO and mux
        ts_scl = TSTriple(1)
        self.specials += ts_scl.get_tristate(pads.scl)

        status = Signal()
        self.comb += self.gpio_in.status[0].eq(status)

        self.specials += MultiReg(ts_scl.i, status)
        self.comb += [
            If(self.gpio_enable.storage, ts_scl.o.eq(self.gpio_out.storage[0]),
               ts_scl.oe.eq(self.gpio_oe.storage[0])).Else(
                   ts_scl.o.eq(programmer.scl), ts_scl.oe.eq(1))
        ]

        # SDA GPIO and mux
        ts_sda = TSTriple(1)
        self.specials += ts_sda.get_tristate(pads.sda)

        status = Signal()
        self.comb += self.gpio_in.status[1].eq(status)

        self.specials += MultiReg(ts_sda.i, status)
        self.comb += [
            If(self.gpio_enable.storage, ts_sda.o.eq(self.gpio_out.storage[1]),
               ts_sda.oe.eq(self.gpio_oe.storage[1])).Else(
                   ts_sda.o.eq(0), ts_sda.oe.eq(~programmer.sda_o))
        ]
        self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper")

        # Error reporting
        collision_cdc = BlindTransfer("helper", "sys")
        self.submodules += collision_cdc
        self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy)

        nack_cdc = PulseSynchronizer("helper", "sys")
        self.submodules += nack_cdc
        self.comb += nack_cdc.i.eq(programmer.nack)

        for n, trig in enumerate([collision_cdc.o, nack_cdc.o]):
            self.sync += [
                If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)),
                If(trig, self.errors.w[n].eq(1))
            ]
Exemplo n.º 5
0
    def __init__(self, pads, debounce_ms=5):
        self.background = ModuleDoc("""Matrix Keyboard Driver
A hardware key scanner that can run even when the CPU is powered down or stopped.

The hardware is assumed to be a matrix of switches, divided into rows and columns.
The number of rows and columns is inferred by the number of pins dedicated in
the `pads` record.

The rows are inputs, and require a `PULLDOWN` to be inferred on the pins, to prevent
them from floating when the keys are not pressed. Note that `PULLDOWN` is not offered
on all FPGA architectures, but at the very least is present on 7-Series FPGAs.

The columns are driven by tristate drivers. The state of the columns is either driven
high (`1`), or hi-Z (`Z`).

The KeyScan module also expects a `kbd` clock domain. The preferred implementation
makes this a 32.768KHz always-on clock input with no PLL. This allows the keyboard
module to continue scanning even if the CPU is powered down.

Columns are scanned sequentially using a `kbd`-domain state machine by driving each
column in order. When a column is driven, its electrical state goes from `hi-z` to `1`.
The rows are then sampled with each column scan state. Since each row has
a pulldown on it, if no keys are hit, the result is `0`. When a key is pressed, it
will short a row to a column, and the `1` driven on a column will flip the row
state to a `1`, thus registering a key press.

Columns are driven for a minimum period of time known as a `settling` period. The
settling time must be at least 2 because a Multireg (2-stage synchronizer) is used
to sample the data on the receiving side. In this module, settling time is fixed
to `4` cycles.

Thus a 1 in the `rowdat` registers indicate the column intersection that was active for
a given row in the matrix.

There is also a row shadow register that is maintained by the hardware. The row shadow
is used to maintain the previous state of the key matrix, so that a "key press change"
interrupt can be generated.

There is also a `keypressed` interrupt which fires every time there is a change in
the row registers.

`debounce` is the period in ms for the keyboard matrix to stabilize before triggering
an interrupt.
        """)
        rows_unsync = pads.row
        cols        = Signal(pads.col.nbits)

        self.uart_inject = Signal(8) # character to inject from the UART
        self.inject_strobe = Signal() # on rising edge, latch uart_inject and raise an interrupt
        self.uart_char = CSRStatus(9, fields = [
            CSRField("char", size=8, description="character value being injected"),
            CSRField("stb", size=1, description="FIFO has readable characters"),
        ])
        self.submodules.injectfifo = injectfifo = fifo.SyncFIFOBuffered(width=8, depth=16)
        self.comb += [
            injectfifo.din.eq(self.uart_inject),
            injectfifo.we.eq(self.inject_strobe),

            #self.uart_char.fields.char.eq(self.uart_inject),
            self.uart_char.fields.char.eq(injectfifo.dout),
            self.uart_char.fields.stb.eq(injectfifo.readable),
            injectfifo.re.eq(self.uart_char.we),
        ]
        for c in range(0, cols.nbits):
            cols_ts = TSTriple(1)
            self.specials += cols_ts.get_tristate(pads.col[c])
            self.comb += cols_ts.oe.eq(cols[c])
            self.comb += cols_ts.o.eq(1)

        # row and col are n-bit signals that correspond to the row and columns of the keyboard matrix
        # each row will generate a column register with the column result in it
        rows = Signal(rows_unsync.nbits)
        self.specials += MultiReg(rows_unsync, rows, "kbd")

        # setattr(self, name, object) is the same as self.name = object, except in this case "name" can be dynamically generated
        # this is necessary here because we CSRStatus is not iterable, so we have to manage the attributes manually
        for r in range(0, rows.nbits):
            setattr(self, "row" + str(r) + "dat", CSRStatus(cols.nbits, name="row" + str(r) + "dat", description="""Column data for the given row"""))

        settling = 4  # 4 cycles to settle: 2 cycles for MultiReg stabilization + slop. Must be > 2, and a power of 2
        colcount = Signal(max=(settling*cols.nbits+2))

        update_shadow = Signal()
        reset_scan    = Signal()
        scan_done     = Signal()
        col_r         = Signal(cols.nbits)
        scan_done_sys = Signal()
        self.specials += MultiReg(scan_done, scan_done_sys)
        for r in range(0, rows.nbits):
            row_scan = Signal(cols.nbits)
            row_scan_sys = Signal(cols.nbits)
            # below is in sysclock domain; row_scan is guaranteed stable by state machine sequencing when scan_done gating is enabled
            self.sync += [
                row_scan_sys.eq(row_scan), # loosen up the clock domain crossing timing
                If(scan_done_sys,
                    getattr(self, "row" + str(r) + "dat").status.eq(row_scan_sys)
                ).Else(
                    getattr(self, "row" + str(r) + "dat").status.eq(getattr(self, "row" + str(r) + "dat").status)
                )
            ]
            self.sync.kbd += [
                If(reset_scan,
                   row_scan.eq(0)
                ).Else(
                    If(rows[r] & (colcount[0:2] == 3),  # sample row only on the 4th cycle of colcount
                       row_scan.eq(row_scan | col_r)
                    ).Else(
                        row_scan.eq(row_scan)
                    )
                )
            ]

            rowshadow = Signal(cols.nbits)
            self.sync.kbd += If(update_shadow, rowshadow.eq(row_scan)).Else(rowshadow.eq(rowshadow))

            setattr(self, "row_scan" + str(r), row_scan)
            setattr(self, "rowshadow" + str(r), rowshadow)
            # create a simple, one-scan delayed version of row_scan for debouncing purposes
            row_debounce = Signal(cols.nbits)
            self.sync.kbd += [
                If(scan_done,
                    row_debounce.eq(row_scan),
                ).Else(
                    row_debounce.eq(row_debounce),
                )
            ]
            setattr(self, "row_debounce" + str(r), row_debounce)

        pending_kbd = Signal()
        pending_kbd_f = Signal()
        key_ack = Signal()
        self.sync.kbd += [
            pending_kbd_f.eq(pending_kbd),
            colcount.eq(colcount + 1),
            scan_done.eq(0),
            update_shadow.eq(0),
            reset_scan.eq(0),
            If(colcount == (settling*cols.nbits+2), colcount.eq(0)),
            If(colcount == (settling*cols.nbits), scan_done.eq(1)),
            # Only update the shadow if the pending bit has been cleared (e.g., CPU has acknowledged
            # it has fetched the current key state)
            If(~pending_kbd & pending_kbd_f,
                If(colcount == (settling * cols.nbits + 1),
                    update_shadow.eq(1),
                    key_ack.eq(0),
                ).Else(
                    key_ack.eq(1),
                )
            ).Else(
                If(colcount == (settling*cols.nbits+1),
                    update_shadow.eq(key_ack),
                    key_ack.eq(0),
                ).Else(
                    key_ack.eq(key_ack)
                )
            ),
            If(colcount == (settling*cols.nbits+2), reset_scan.eq(1)),
        ]

        # Drive the columns based on the colcount counter
        self.submodules.coldecoder = Decoder(cols.nbits)
        self.comb += [
            self.coldecoder.i.eq(colcount[log2_int(settling):]),
            self.coldecoder.n.eq(~(colcount < settling*cols.nbits)),
            cols.eq(self.coldecoder.o)
        ]
        self.sync.kbd += col_r.eq(self.coldecoder.o)

        self.submodules.ev = EventManager()
        self.ev.keypressed = EventSourcePulse(description="Triggered every time there is a difference in the row state") # Rising edge triggered
        self.ev.inject = EventSourcePulse(description="Key injection request received")
        self.ev.finalize()
        self.sync += [
            self.ev.inject.trigger.eq(self.inject_strobe)
        ]

        # zero state auto-clear: the "delta" methodology gets stuck if the initial sampling state of the keyboard matrix isn't 0. this fixes that.
        rows_nonzero = Signal(rows.nbits)
        col_zeros = Signal(cols.nbits) # form multi-bit zeros of the right width, otherwise we get 1'd0 as the rhs of equality statements
        row_zeros = Signal(rows.nbits)
        for r in range(0, rows.nbits):
            self.sync.kbd +=  rows_nonzero[r].eq(getattr(self, "row_scan" + str(r)) != col_zeros)
        all_zeros = Signal()
        clear_deltas = Signal()
        self.comb += all_zeros.eq(rows_nonzero == row_zeros)
        clear_kbd = Signal()
        self.submodules.clear_sync = BlindTransfer("sys", "kbd")
        self.comb += [
            self.clear_sync.i.eq(self.ev.keypressed.clear),
            clear_kbd.eq(self.clear_sync.o)
        ]

        # debounce timers and clocks
        debounce_clocks = int((debounce_ms * 0.001) * 32768.0)
        debounce_timer  = Signal(max=(debounce_clocks + 1))
        debounced = Signal()
        # Extract any changes just before the shadow takes its new values
        rowdiff = Signal(rows.nbits)
        for r in range(0, rows.nbits):
            self.sync.kbd += [
                If(clear_deltas,
                    rowdiff[r].eq(0),
                ).Elif(debounced, # was scan_done
                   rowdiff[r].eq( ~((getattr(self, "row_scan" + str(r)) ^ getattr(self, "rowshadow" + str(r))) == 0) )
                ).Else(
                    rowdiff[r].eq(rowdiff[r])
                )
            ]

        # debouncing:
        #   1. compute the delta of the current scan vs. previous scan
        #   2. if the delta is non-zero, start a debounce timer.
        #   3. Count down as long as the delta remains zero; if the delta is non-zero again, reset the timer.
        #   4. When the timer hits zero, latch the final value for sampling to the CPU
        rowchanging = Signal(rows.nbits)
        for r in range(0, rows.nbits):
            self.sync.kbd += [
                If(clear_deltas,
                    rowchanging[r].eq(0),
                ).Elif(scan_done,
                    rowchanging[r].eq( ~((getattr(self, "row_scan" + str(r)) ^ getattr(self, "row_debounce" + str(r))) == 0) )
                ).Else(
                    rowchanging[r].eq(rowchanging[r])
                )
            ]

        db_fsm = ClockDomainsRenamer("kbd")(FSM(reset_state="IDLE"))
        self.submodules += db_fsm
        db_fsm.act("IDLE",
            If(rowchanging != 0,
                NextValue(debounce_timer, debounce_clocks),
                NextState("DEBOUNCING")
            )
        )
        db_fsm.act("DEBOUNCING",
            If(rowchanging == 0,
                NextValue(debounce_timer, debounce_timer - 1),
                If(debounce_timer == 0,
                    NextState("DEBOUNCED")
                )
            ).Else(
                NextValue(debounce_timer, debounce_clocks),
                NextState("DEBOUNCING")
            )
        )
        db_fsm.act("DEBOUNCED",
            If(scan_done,
                debounced.eq(1),
                NextState("IDLE")
            )
        )
        self.comb += clear_deltas.eq(all_zeros & clear_kbd & db_fsm.ongoing("IDLE"))

        # Fire an interrupt during the reset_scan phase.
        changed  = Signal()
        changed_sys = Signal()
        kp_r  = Signal()
        changed_kbd = Signal()
        debounced_d = Signal()
        self.sync.kbd += debounced_d.eq(debounced)
        self.kbd_wakeup = Signal()
        self.comb += changed.eq(debounced_d & (rowdiff != 0))
        self.sync.kbd += [
            If(changed,
                changed_kbd.eq(1)
            ).Elif(key_ack,
                changed_kbd.eq(0)
            ).Else(
                changed_kbd.eq(changed_kbd)
            )
        ]
        self.comb += self.kbd_wakeup.eq(changed_kbd | changed)

        self.specials += MultiReg(changed_kbd, changed_sys)
        self.sync += kp_r.eq(changed_sys)
        self.comb += self.ev.keypressed.trigger.eq(changed_sys & ~kp_r)

        self.submodules.pending_sync = BlindTransfer("sys", "kbd")
        # pulse-convert pending into a single pulse on the falling edge -- otherwise we get a pulse train
        kp_pending_sys = Signal()
        kp_pending_sys_r = Signal()
        self.sync += [
            kp_pending_sys.eq(self.ev.keypressed.pending),
            kp_pending_sys_r.eq(kp_pending_sys),
        ]
        self.comb += [
            self.pending_sync.i.eq(~kp_pending_sys & kp_pending_sys_r),
            pending_kbd.eq(self.pending_sync.o),
        ]
Exemplo n.º 6
0
    def __init__(self, platform):
        self.bus = bus = wishbone.Interface()
        wdata = Signal(32)
        wmask = Signal(4)
        wdata_we = Signal()
        wdata_avail = Signal()
        wdata_ready = Signal()
        self.sync.clk50 += [
            wdata_avail.eq(bus.cyc & bus.stb & bus.we),
            If(
                bus.cyc & bus.stb & bus.we & ~bus.ack,
                If(
                    wdata_ready,
                    wdata.eq(bus.dat_w),
                    wmask.eq(bus.sel),
                    wdata_we.eq(1),
                    bus.ack.eq(
                        1
                    ),  #### TODO check that this works with the clk50->clk100 domain crossing
                ).Else(
                    wdata_we.eq(0),
                    bus.ack.eq(0),
                )).Else(
                    wdata_we.eq(0),
                    bus.ack.eq(0),
                )
        ]

        self.key_re = Signal(8)
        for k in range(0, 8):
            setattr(
                self, "key" + str(k),
                CSRStorage(32,
                           name="key" + str(k),
                           description="""secret key word {}""".format(k)))
            self.key_re[k].eq(getattr(self, "key" + str(k)).re)

        self.config = CSRStorage(
            description="Configuration register for the HMAC block",
            fields=[
                CSRField("sha_en",
                         size=1,
                         description="Enable the SHA256 core"),
                CSRField("endian_swap",
                         size=1,
                         description="Swap the endianness on the input data"),
                CSRField(
                    "digest_swap",
                    size=1,
                    description="Swap the endianness on the output digest"),
                CSRField("hmac_en", size=1,
                         description="Enable the HMAC core"),
                CSRField(
                    "reset",
                    size=1,
                    description=
                    "Resets the hardware. Power must be on for this to take effect.",
                    pulse=True)
            ])
        control_latch = Signal(self.config.size)
        ctrl_freeze = Signal()
        self.sync.clk50 += [
            If(ctrl_freeze, control_latch.eq(control_latch)).Else(
                control_latch.eq(self.config.storage))
        ]
        reset_50 = Signal()
        self.submodules.resetter = BlindTransfer("sys", "clk50")
        self.comb += [
            self.resetter.i.eq(self.config.fields.reset),
            reset_50.eq(self.resetter.o)
        ]
        rescnt = Signal(max=16, reset=15)
        sw_reset = Signal()
        self.sync.clk50 += [
            If(reset_50, rescnt.eq(15)).Elif(rescnt > 0,
                                             rescnt.eq(rescnt - 1)).Else(
                                                 rescnt.eq(rescnt)),
            sw_reset.eq(rescnt != 0),
        ]

        self.command = CSRStorage(
            description="Command register for the HMAC block",
            fields=[
                CSRField("hash_start",
                         size=1,
                         description=
                         "Writing a 1 indicates the beginning of hash data",
                         pulse=True),
                CSRField("hash_process",
                         size=1,
                         description="Writing a 1 digests the hash data",
                         pulse=True),
            ])

        self.wipe = CSRStorage(
            32,
            description=
            "wipe the secret key using the written value. Wipe happens upon write."
        )

        for k in range(0, 8):
            setattr(
                self, "digest" + str(k),
                CSRStatus(32,
                          name="digest" + str(k),
                          description="""digest word {}""".format(k)))

        self.msg_length = CSRStatus(
            size=64, description="Length of digested message, in bits")
        self.error_code = CSRStatus(size=32, description="Error code")

        self.submodules.ev = EventManager()
        self.ev.err_valid = EventSourcePulse(
            description="Error flag was generated")
        self.ev.fifo_full = EventSourcePulse(description="FIFO is full")
        self.ev.hash_done = EventSourcePulse(description="HMAC is done")
        self.ev.sha256_done = EventSourcePulse(description="SHA256 is done")
        self.ev.finalize()
        err_valid = Signal()
        err_valid_r = Signal()
        fifo_full = Signal()
        fifo_full_r = Signal()
        hmac_hash_done = Signal()
        sha256_hash_done = Signal()
        self.sync += [
            err_valid_r.eq(err_valid),
            fifo_full_r.eq(fifo_full),
        ]
        self.comb += [
            self.ev.err_valid.trigger.eq(~err_valid_r & err_valid),
            self.ev.fifo_full.trigger.eq(~fifo_full_r & fifo_full),
            self.ev.hash_done.trigger.eq(hmac_hash_done),
            self.ev.sha256_done.trigger.eq(sha256_hash_done),
        ]

        # At a width of 32 bits, an 36kiB fifo is 1024 entries deep
        fifo_wvalid = Signal()
        fifo_wdata_mask = Signal(36)
        fifo_rready = Signal()
        fifo_rdata_mask = Signal(36)
        self.fifo = CSRStatus(description="FIFO status",
                              fields=[
                                  CSRField("read_count",
                                           size=10,
                                           description="read pointer"),
                                  CSRField("write_count",
                                           size=10,
                                           description="write pointer"),
                                  CSRField("read_error",
                                           size=1,
                                           description="read error occurred"),
                                  CSRField("write_error",
                                           size=1,
                                           description="write error occurred"),
                                  CSRField("almost_full",
                                           size=1,
                                           description="almost full"),
                                  CSRField("almost_empty",
                                           size=1,
                                           description="almost empty"),
                              ])
        fifo_rvalid = Signal()
        fifo_empty = Signal()
        fifo_wready = Signal()
        fifo_full_local = Signal()
        self.comb += fifo_rvalid.eq(~fifo_empty)
        self.comb += fifo_wready.eq(~fifo_full_local)
        self.specials += Instance(
            "FIFO36E1",
            p_DATA_WIDTH=36,
            p_ALMOST_EMPTY_OFFSET=8,
            p_ALMOST_FULL_OFFSET=8,
            p_DO_REG=1,
            p_FIRST_WORD_FALL_THROUGH="TRUE",
            p_EN_SYN="FALSE",
            i_RDCLK=ClockSignal("clk50"),
            i_WRCLK=ClockSignal("clk50"),
            i_RST=ResetSignal("clk50") | sw_reset,
            o_FULL=fifo_full_local,
            i_WREN=fifo_wvalid,
            i_DI=fifo_wdata_mask[:32],
            i_DIP=fifo_wdata_mask[32:],
            o_EMPTY=fifo_empty,
            i_RDEN=fifo_rready & fifo_rvalid,
            o_DO=fifo_rdata_mask[:32],
            o_DOP=fifo_rdata_mask[32:],
            o_RDCOUNT=self.fifo.fields.read_count,
            o_RDERR=self.fifo.fields.read_error,
            o_WRCOUNT=self.fifo.fields.write_count,
            o_WRERR=self.fifo.fields.write_error,
            o_ALMOSTFULL=self.fifo.fields.almost_full,
            o_ALMOSTEMPTY=self.fifo.fields.almost_empty,
        )

        key_re_50 = Signal(8)
        for k in range(0, 8):
            setattr(self.submodules, 'keyre50_' + str(k),
                    BlindTransfer("sys", "clk50"))
            getattr(self, 'keyre50_' + str(k)).i.eq(
                getattr(self, 'key' + str(k)).re)
            self.comb += key_re_50[k].eq(getattr(self, 'keyre50_' + str(k)).o)

        hash_start_50 = Signal()
        self.submodules.hashstart = BlindTransfer("sys", "clk50")
        self.comb += [
            self.hashstart.i.eq(self.command.fields.hash_start),
            hash_start_50.eq(self.hashstart.o)
        ]

        hash_proc_50 = Signal()
        self.submodules.hashproc = BlindTransfer("sys", "clk50")
        self.comb += [
            self.hashproc.i.eq(self.command.fields.hash_process),
            hash_proc_50.eq(self.hashproc.o)
        ]

        wipe_50 = Signal()
        self.submodules.wipe50 = BlindTransfer("sys", "clk50")
        self.comb += [
            self.wipe50.i.eq(self.wipe.re),
            wipe_50.eq(self.wipe50.o)
        ]

        self.specials += Instance(
            "sha2_litex",
            i_clk_i=ClockSignal("clk50"),
            i_rst_ni=~(ResetSignal("clk50") | sw_reset),
            i_secret_key_0=self.key0.storage,
            i_secret_key_1=self.key1.storage,
            i_secret_key_2=self.key2.storage,
            i_secret_key_3=self.key3.storage,
            i_secret_key_4=self.key4.storage,
            i_secret_key_5=self.key5.storage,
            i_secret_key_6=self.key6.storage,
            i_secret_key_7=self.key7.storage,
            i_secret_key_re=key_re_50,
            i_reg_hash_start=hash_start_50,
            i_reg_hash_process=hash_proc_50,
            o_ctrl_freeze=ctrl_freeze,
            i_sha_en=control_latch[0],
            i_endian_swap=control_latch[1],
            i_digest_swap=control_latch[2],
            i_hmac_en=control_latch[3],
            o_reg_hash_done=hmac_hash_done,
            o_sha_hash_done=sha256_hash_done,
            i_wipe_secret_re=wipe_50,
            i_wipe_secret_v=self.wipe.storage,
            o_digest_0=self.digest0.status,
            o_digest_1=self.digest1.status,
            o_digest_2=self.digest2.status,
            o_digest_3=self.digest3.status,
            o_digest_4=self.digest4.status,
            o_digest_5=self.digest5.status,
            o_digest_6=self.digest6.status,
            o_digest_7=self.digest7.status,
            o_msg_length=self.msg_length.status,
            o_error_code=self.error_code.status,
            i_msg_fifo_wdata=wdata,
            i_msg_fifo_write_mask=wmask,
            i_msg_fifo_we=wdata_we,
            i_msg_fifo_req=wdata_avail,
            o_msg_fifo_gnt=wdata_ready,
            o_local_fifo_wvalid=fifo_wvalid,
            i_local_fifo_wready=fifo_wready,
            o_local_fifo_wdata_mask=fifo_wdata_mask,
            i_local_fifo_rvalid=fifo_rvalid,
            o_local_fifo_rready=fifo_rready,
            i_local_fifo_rdata_mask=fifo_rdata_mask,
            o_err_valid=err_valid,
            i_err_valid_pending=self.ev.err_valid.pending,
            o_fifo_full_event=fifo_full,
        )

        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "hmac", "rtl",
                         "hmac_pkg.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "hmac", "rtl",
                         "sha2.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "hmac", "rtl",
                         "sha2_pad.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "prim", "rtl",
                         "prim_packer.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "hmac", "rtl",
                         "hmac_core.sv"))
        platform.add_source(
            os.path.join("deps", "gateware", "gateware", "sha2_litex.sv"))
Exemplo n.º 7
0
    def __init__(self, platform):
        self.bus = bus = wishbone.Interface()
        wdata = Signal(64)
        wmask = Signal(8)
        wdata_we = Signal()
        wdata_avail = Signal()
        wdata_ready = Signal()
        ack_lsb = Signal()
        ack_lsb_r = Signal()
        ack_msb = Signal()
        ack_msb_r = Signal()
        self.sync.clk50 += [
            wdata_avail.eq(
                bus.cyc & bus.stb & bus.we & bus.adr[0] == 0
            ),  # transfer on an even write, so top word must be written first
            If(
                bus.cyc & bus.stb & bus.we & ~bus.ack,
                If(
                    bus.adr[0] == 0,
                    wdata[:32].eq(bus.dat_w),
                    wdata[32:].eq(wdata[32:]),
                    wmask[:4].eq(bus.sel),
                    wmask[4:].eq(wmask[4:]),
                    wdata_we.eq(1),  # only commit on even words
                    ack_lsb.eq(1),
                ).Else(
                    wdata[:32].eq(wdata[:32]),
                    wdata[32:].eq(bus.dat_w),
                    wmask[:4].eq(wmask[:4]),
                    wmask[4:].eq(bus.sel),
                    ack_msb.eq(1),
                ),
            ).Else(
                wdata.eq(0),
                wmask.eq(0),
                wdata_we.eq(0),
                ack_lsb.eq(0),
                ack_msb.eq(0),
            )
        ]
        self.sync += [
            ack_lsb_r.eq(ack_lsb),
            ack_msb_r.eq(ack_msb),
            bus.ack.eq(~ack_lsb_r & ack_lsb
                       | ~ack_msb_r & ack_msb)  # single-cycle acks only!
        ]

        self.config = CSRStorage(
            description="Configuration register for the HMAC block",
            fields=[
                CSRField("sha_en",
                         size=1,
                         description="Enable the SHA512 core"),
                CSRField("endian_swap",
                         size=1,
                         description="Swap the endianness on the input data"),
                CSRField(
                    "digest_swap",
                    size=1,
                    description="Swap the endianness on the output digest"),
                CSRField(
                    "select_256",
                    size=1,
                    description="Select SHA512/256 IV constants when set to `1`"
                )
            ])
        control_latch = Signal(self.config.size)
        ctrl_freeze = Signal()
        sha_en_50 = Signal()
        self.sync.clk50 += [
            If(ctrl_freeze, control_latch.eq(control_latch)).Else(
                control_latch.eq(self.config.storage)),
            sha_en_50.eq(self.config.fields.sha_en),
        ]
        self.command = CSRStorage(
            description="Command register for the HMAC block",
            fields=[
                CSRField("hash_start",
                         size=1,
                         description=
                         "Writing a 1 indicates the beginning of hash data",
                         pulse=True),
                CSRField("hash_process",
                         size=1,
                         description="Writing a 1 digests the hash data",
                         pulse=True),
            ])

        for k in range(0, 8):
            setattr(
                self, "digest" + str(k),
                CSRStatus(64,
                          name="digest" + str(k),
                          description="""digest word {}""".format(k)))

        self.msg_length = CSRStatus(
            size=64,
            description="Bottom 64 bits of length of digested message, in bits"
        )

        self.submodules.ev = EventManager()
        self.ev.err_valid = EventSourcePulse(
            description="Error flag was generated")
        self.ev.fifo_full = EventSourcePulse(description="FIFO is full")
        self.ev.sha512_done = EventSourcePulse(description="SHA512 is done")
        self.ev.finalize()
        err_valid = Signal()
        err_valid_r = Signal()
        fifo_full = Signal()
        fifo_full_r = Signal()
        sha512_hash_done = Signal()
        self.sync += [
            err_valid_r.eq(err_valid),
            fifo_full_r.eq(fifo_full),
        ]
        self.comb += [
            self.ev.err_valid.trigger.eq(~err_valid_r & err_valid),
            self.ev.fifo_full.trigger.eq(~fifo_full_r & fifo_full),
            self.ev.sha512_done.trigger.eq(sha512_hash_done),
        ]

        # At a width of 64 bits, an 36kiB fifo is 512 entries deep
        fifo_wvalid = Signal()
        fifo_wdata_mask = Signal(72)
        fifo_rready = Signal()
        fifo_rdata_mask = Signal(72)
        self.fifo = CSRStatus(
            description="FIFO status",
            fields=[
                CSRField("read_count", size=9, description="read pointer"),
                CSRField("write_count", size=9, description="write pointer"),
                CSRField("read_error",
                         size=1,
                         description="read error occurred"),
                CSRField("write_error",
                         size=1,
                         description="write error occurred"),
                CSRField("almost_full", size=1, description="almost full"),
                CSRField("almost_empty", size=1, description="almost empty"),
                CSRField("running",
                         size=1,
                         description=
                         "hash engine is running and controls are locked out"),
            ])
        ctrl_freeze_sys = Signal()
        self.specials += MultiReg(ctrl_freeze, ctrl_freeze_sys)
        self.comb += self.fifo.fields.running.eq(ctrl_freeze_sys)

        fifo_rvalid = Signal()
        fifo_empty = Signal()
        fifo_wready = Signal()
        fifo_full_local = Signal()
        self.comb += fifo_rvalid.eq(~fifo_empty)
        self.comb += fifo_wready.eq(~fifo_full_local)
        self.specials += Instance(
            "FIFO36_72",
            #            p_DATA_WIDTH=72,
            p_ALMOST_EMPTY_OFFSET=8,
            p_ALMOST_FULL_OFFSET=8,
            p_DO_REG=1,
            p_FIRST_WORD_FALL_THROUGH="TRUE",
            p_EN_SYN="FALSE",
            i_RDCLK=ClockSignal("clk50"),
            i_WRCLK=ClockSignal("clk50"),
            i_RST=ResetSignal("clk50"),
            o_FULL=fifo_full_local,
            i_WREN=fifo_wvalid,
            i_DI=fifo_wdata_mask[:64],
            i_DIP=fifo_wdata_mask[64:],
            o_EMPTY=fifo_empty,
            i_RDEN=fifo_rready & fifo_rvalid,
            o_DO=fifo_rdata_mask[:64],
            o_DOP=fifo_rdata_mask[64:],
            o_RDCOUNT=self.fifo.fields.read_count,
            o_RDERR=self.fifo.fields.read_error,
            o_WRCOUNT=self.fifo.fields.write_count,
            o_WRERR=self.fifo.fields.write_error,
            o_ALMOSTFULL=self.fifo.fields.almost_full,
            o_ALMOSTEMPTY=self.fifo.fields.almost_empty,
        )

        hash_start_50 = Signal()
        self.submodules.hashstart = BlindTransfer("sys", "clk50")
        self.comb += [
            self.hashstart.i.eq(self.command.fields.hash_start),
            hash_start_50.eq(self.hashstart.o)
        ]

        hash_proc_50 = Signal()
        self.submodules.hashproc = BlindTransfer("sys", "clk50")
        self.comb += [
            self.hashproc.i.eq(self.command.fields.hash_process),
            hash_proc_50.eq(self.hashproc.o)
        ]

        self.specials += Instance(
            "sha512_litex",
            i_clk_i=ClockSignal("clk50"),
            i_rst_ni=~ResetSignal("clk50"),
            i_reg_hash_start=hash_start_50,
            i_reg_hash_process=hash_proc_50,
            o_ctrl_freeze=ctrl_freeze,
            i_sha_en=sha_en_50,
            i_endian_swap=control_latch[1],
            i_digest_swap=control_latch[2],
            i_hash_select_256=control_latch[3],
            o_sha_hash_done=sha512_hash_done,
            o_digest_0=self.digest0.status,
            o_digest_1=self.digest1.status,
            o_digest_2=self.digest2.status,
            o_digest_3=self.digest3.status,
            o_digest_4=self.digest4.status,
            o_digest_5=self.digest5.status,
            o_digest_6=self.digest6.status,
            o_digest_7=self.digest7.status,
            o_msg_length=self.msg_length.status,
            i_msg_fifo_wdata=wdata,
            i_msg_fifo_write_mask=wmask,
            i_msg_fifo_we=wdata_we,
            i_msg_fifo_req=wdata_avail,
            o_msg_fifo_gnt=wdata_ready,
            o_local_fifo_wvalid=fifo_wvalid,
            i_local_fifo_wready=fifo_wready,
            o_local_fifo_wdata_mask=fifo_wdata_mask,
            i_local_fifo_rvalid=fifo_rvalid,
            o_local_fifo_rready=fifo_rready,
            i_local_fifo_rdata_mask=fifo_rdata_mask,
            o_err_valid=err_valid,
            i_err_valid_pending=self.ev.err_valid.pending,
            o_fifo_full_event=fifo_full,
        )

        platform.add_source(
            os.path.join("deps", "gateware", "gateware", "sha512",
                         "hmac512_pkg.sv"))
        platform.add_source(
            os.path.join("deps", "gateware", "gateware", "sha512",
                         "sha512.sv"))
        platform.add_source(
            os.path.join("deps", "gateware", "gateware", "sha512",
                         "sha512_pad.sv"))
        platform.add_source(
            os.path.join("deps", "gateware", "gateware", "sha512",
                         "prim_packer512.sv"))
        platform.add_source(
            os.path.join("deps", "gateware", "gateware", "sha512_litex.sv"))
Exemplo n.º 8
0
    def __init__(self, platform):
        self.intro = ModuleDoc(title="ChaCha cipher", body="""
This is the Litex wrapper for the ChaCha block vendored in from
https://github.com/secworks/chacha/commit/2636e87a7e695bd3fa72981b43d0648c49ecb10d

        """)
        self.seed = Signal(32)  # input / a steady drip of new data to add to the entropy pool
        self.seed_req = Signal()  # output / indicates this block is requesting a seed value.
        self.seed_gnt = Signal()  # input / indicates seed request has been granted. this causes _req to drop, starting a new cycle.
        self.userdata = Signal(32) # input / whatever user-provided data for seeding (optional)
        self.seed_now = Signal() # input / a single-cycle pulse that applies the userdata value. ignored until ready is asserted.
        self.ready = Signal()    # output / indicates all seeding operations are done
        self.reseed_interval = Signal(12) # input / indicates how many ChaCha rounds we can generate before demanding a reseed. 0 means never auto-reseed.

        self.advance_a = Signal()   # input / a single-cycle pulse that advances the value of output_a
        self.output_a = Signal(32)  # output / one of two output ports
        self.valid_a = Signal()     # output / when high, the value on output_a is valid

        self.advance_b = Signal()   # input / a single-cycle pulse that advances the value of output_b
        self.output_b = Signal(32)  # output / second of two output ports
        self.valid_b = Signal()     # output / when high, the value on output_a is valid

        self.selfmix_ena = Signal()     # input / enables opportunistic self-mixing in the background
        self.selfmix_interval = Signal(16)  # input / sysclk cycles in between opportunistic self mixings; for power savings

        # rename so we can easily isolate non-critical path input to avoid false timing dependencies
        userdata_local = Signal(self.userdata.nbits)
        seed_now_local = Signal()
        reseed_interval_local = Signal(self.reseed_interval.nbits)
        selfmix_ena_local = Signal()
        selfmix_interval_local = Signal(self.selfmix_interval.nbits)
        self.comb += [
            userdata_local.eq(self.userdata),
            seed_now_local.eq(self.seed_now),
            reseed_interval_local.eq(self.reseed_interval),
            selfmix_ena_local.eq(self.selfmix_ena),
            selfmix_interval_local.eq(self.selfmix_interval),
        ]

        # local signals from the output of the system
        advance_a_local = Signal()
        output_a_local = Signal(32)
        valid_a_local = Signal()
        advance_b_local = Signal()
        output_b_local = Signal(32)
        valid_b_local = Signal()
        # now, create a pipeline (more correctly, a depth-1 fifo) to isolate the CPU's r/w path from the shifter logic
        # the problem is that we're pulsing the advancement of the output shifter based upon a single-cycle combinational path
        # that originates deep inside the CPU load/store pipe. This becomes an unecessary critical path, so introducing
        # this logic here will relax that over-constraint
        outafsm = FSM(reset_state="FILL")
        self.submodules += outafsm
        outafsm.act("FILL",
            If(valid_a_local,
                NextValue(self.output_a, output_a_local),
                NextValue(self.valid_a, valid_a_local),
                NextState("PIPE"),
                NextValue(advance_a_local, 1)
            ).Else(
                NextValue(advance_a_local, 0)
            )
        )
        outafsm.act("PIPE",
            If(self.advance_a,
                NextValue(self.output_a, output_a_local),
                NextValue(self.valid_a, valid_a_local),
                NextValue(advance_a_local, 1)
            ).Else(
                NextValue(advance_a_local, 0)
            ),
            If(~self.valid_a,
                NextState("FILL")
            )
        )
        outbfsm = FSM(reset_state="FILL")
        self.submodules += outbfsm
        outbfsm.act("FILL",
            If(valid_b_local,
                NextValue(self.output_b, output_b_local),
                NextValue(self.valid_b, valid_b_local),
                NextState("PIPE"),
                NextValue(advance_b_local, 1),
            ).Else(
                NextValue(advance_b_local, 0)
            )
        )
        outbfsm.act("PIPE",
            If(self.advance_b,
                NextValue(self.output_b, output_b_local),
                NextValue(self.valid_b, valid_b_local),
                NextValue(advance_b_local, 1),
            ).Else(
                NextValue(advance_b_local, 0),
            ),
            If(~self.valid_b,
                NextState("FILL")
            )
        )

        state = Signal(384) # key + iv + ctr
        state_rot = Signal()
        holding_buf = Signal(512)
        holding_buf_shift_by_1 = Signal()
        holding_buf_shift_by_2 = Signal()
        holding_buf_load = Signal()
        advance_block = Signal()
        selfmix_ctr = Signal(self.selfmix_interval.nbits)

        self.sync += [
            If(state_rot | (self.ready & seed_now_local),
                If(seed_now_local,
                    state.eq(Cat(state[-32:] ^ self.seed ^ userdata_local, state[:-32]))
                ).Else(
                    state.eq(Cat(state[-32:] ^ self.seed, state[:-32]))
                )
            ).Else(
                state.eq(state)
            )
        ]

        # verilog I/Os
        init_50 = Signal()
        next_50 = Signal()
        key = Signal(256)
        iv = Signal(64)
        ctr = Signal(64)
        data_in = Signal(512)
        force_round_50 = Signal()
        data_out_50 = Signal(512)
        ready_50 = Signal()
        valid_50 = Signal()

        # 100MHz-domain signals
        init = Signal()
        next = Signal()
        force_round = Signal()

        # use fixed random data to blind the output of the generator
        din_shift = Signal()
        self.sync += [
            If(din_shift,
                data_in.eq(Cat(data_in[-32:] ^ self.seed, data_in[:-32]))
            ).Else(
                data_in.eq(data_in)
            )
        ]

        # output control
        sentinel = Signal(32, reset=0xDEAD_BEEF) # sentinel value to indicate something is wrong
        hold_init = Signal()
        holdfsm_load = Signal()
        saw_load = Signal()
        clear_saw_load = Signal()
        data_out = Signal(512)
        self.sync += [
            data_out.eq(data_out_50), # bring the output of chacha into the 100mhz domain to meet timing
            If( (holding_buf_load & ~hold_init) | holdfsm_load,
                holding_buf.eq(data_out)
            ).Elif(holding_buf_shift_by_1,
                holding_buf.eq(Cat(sentinel, holding_buf[:-32]))
            ).Elif(holding_buf_shift_by_2,
                holding_buf.eq(Cat(sentinel, sentinel, holding_buf[:-64]))
            ).Else(
                holding_buf.eq(holding_buf)
            ),
            #saw_load.eq(
            #    ((~clear_saw_load & ~holding_buf_load) & saw_load) |
            #    (holding_buf_load & ~clear_saw_load)
            #),
            If(clear_saw_load,
                saw_load.eq(0)
            ).Elif(holding_buf_load,
                saw_load.eq(1)
            ).Else(
                saw_load.eq(saw_load)
            )
        ]
        ### implement the output control logic here
        words_remaining = Signal(max=17, reset=0) # max= is *exclusive*, so you need 1 plus the actual maximum value
        holdfsm = FSM(reset_state="RESET")
        self.submodules += holdfsm
        holdfsm.act("RESET",
            NextValue(hold_init, 0),
            NextValue(words_remaining, 0),
            NextValue(valid_a_local, 0),
            NextValue(valid_b_local, 0),
            If(holding_buf_load,
                NextState("INIT"),
            )
        )
        # structurally, at this point:
        # - ChaCha20 is initialized
        # - holding buffer has 16 words of data
        # - ChaCha20 is already computing the next 16 words, and when it's done, `saw_load` will become 1
        holdfsm.act("INIT",
            clear_saw_load.eq(1), # this "wins" over holding_buf_load, and saw_load should be 0
            advance_block.eq(1), # immediately queue up the next block
            NextValue(hold_init, 1), # this will prevent further auto-initialization of the holding buffer

            NextValue(output_a_local, holding_buf[-32:]),
            NextValue(output_b_local, holding_buf[-64:-32]),
            NextValue(valid_a_local, 1),
            NextValue(valid_b_local, 1),
            NextValue(words_remaining, 14),
            holding_buf_shift_by_2.eq(1),
            NextState("OUTPUT"),
        )
        holdfsm.act("OUTPUT",
            # handle the case of needing a single word
            If( ( # update conditions are either we've received an adavance, or the valid is low (we saw an advance and wasn't able to refill)
                  # if you advance while not valid, well...that's undefined behavior! you will be reading the sentinel value
                  (advance_a_local | ~valid_a_local) & (~advance_b_local & valid_b_local) | # only a needs an update
                  (~advance_a_local & valid_a_local) & (advance_b_local | ~valid_b_local)   # only b needs an update
                ),
                If((advance_a_local | ~valid_a_local) & (~advance_b_local & valid_b_local), # a needs an update
                    NextValue(output_a_local, holding_buf[-32:]),
                    NextValue(valid_a_local, 1),
                ).Else( # if not a, then must be that b needs an update
                    NextValue(output_b_local, holding_buf[-32:]),
                    NextValue(valid_b_local, 1),
                ),
                # either way, we do this stuff to advance the next state
                holding_buf_shift_by_1.eq(1),
                If(words_remaining <= 1,
                    If(saw_load,
                        advance_block.eq(1),
                        holdfsm_load.eq(1),
                        clear_saw_load.eq(1),
                        NextValue(words_remaining, 16),
                    ).Else(
                        NextValue(words_remaining, 0),
                        NextState("WAIT")
                    )
                ).Else(
                    NextValue(words_remaining, words_remaining - 1),
                )
            ).Elif( (advance_a_local | ~valid_a_local) & (advance_b_local | ~valid_b_local), # case of needing two words
                If(words_remaining >= 2,
                    NextValue(output_a_local, holding_buf[-32:]),
                    NextValue(valid_a_local, 1),
                    NextValue(output_b_local, holding_buf[-64:-32]),
                    NextValue(valid_b_local, 1),
                    If(words_remaining == 2, # we're empty, try to pull the ready state; if not available, go wait
                        If(saw_load,
                            advance_block.eq(1),
                            holdfsm_load.eq(1),
                            clear_saw_load.eq(1),
                            NextValue(words_remaining, 16),
                        ).Else(
                            NextValue(words_remaining, 0),
                            NextState("WAIT")
                        )
                    ).Else(
                        holding_buf_shift_by_2.eq(1),
                        NextValue(words_remaining, words_remaining - 2),
                    )
                ).Else( # either 1 or 0 words
                    If(words_remaining == 1,
                        # we have one word remaining, arbitrarily advance a over b
                        NextValue(output_a_local, holding_buf[-32:]),
                        NextValue(valid_a_local, 1),
                        NextValue(output_b_local, sentinel),
                        NextValue(valid_b_local, 0),
                        NextValue(words_remaining, 0),
                    ).Else( # 0 words -- should never hit this case, but handle it anyways
                        NextValue(output_a_local, sentinel),
                        NextValue(valid_a_local, 0),
                        NextValue(output_b_local, sentinel),
                        NextValue(valid_b_local, 0),
                    ),
                    NextState("WAIT")
                )
            ).Else(
                # this should never be reachable: every condition above will reload before we hit zero.
                # but cover this case just in case, so we don't ever get "stuck" here, on a glitch or whatever
                If(words_remaining == 0,
                    If(saw_load,
                        advance_block.eq(1),
                        holdfsm_load.eq(1),
                        clear_saw_load.eq(1),
                        NextValue(words_remaining, 16),
                    ).Else(
                        NextState("WAIT")
                    )
                )
            )
        )
        holdfsm.act("WAIT",
            If(advance_a_local,
                NextValue(output_a_local, sentinel),
                NextValue(valid_a_local, 0)
            ),
            If(advance_b_local,
                NextValue(output_b_local, sentinel),
                NextValue(valid_b_local, 0)
            ),
            If(saw_load,
                advance_block.eq(1),
                holdfsm_load.eq(1),
                clear_saw_load.eq(1),
                NextValue(words_remaining, 16),
                NextState("OUTPUT")
            )
        )


        # seed control
        reseed_ctr = Signal(self.reseed_interval.nbits)
        seed_ctr = Signal(5, reset=16)
        seedfsm = FSM(reset_state="RESET")
        valid = Signal() # valid brought into the sysclk domain as a single pulse
        valid_r = Signal()
        valid_r2 = Signal()
        ready = Signal()
        seed_gnt_rising = Signal()
        seed_gnt_r = Signal()
        self.sync +=  [
            ready.eq(ready_50), # must pipeline this to break a subtle circular dependency that goes from the output to the input of chacha!
            valid_r.eq(valid_50), # cross 50->100
            valid_r2.eq(valid_r),
            seed_gnt_r.eq(self.seed_gnt),
        ]
        self.comb += valid.eq(~valid_r2 & valid_r)
        self.comb += seed_gnt_rising.eq(~seed_gnt_r & self.seed_gnt)
        self.submodules += seedfsm
        seedfsm.act("RESET",
            NextValue(reseed_ctr, 1),
            NextValue(self.ready, 0),
            NextValue(seed_ctr, 13), # seed in 384 bits for key + 1 dummy word to toss the first 0 on the FIFO
            NextState("SEEDING"),
        )
        seedfsm.act("SEEDING",
            If(seed_gnt_rising,
                state_rot.eq(1),
                NextValue(seed_ctr, seed_ctr - 1),
            ),
            If(seed_ctr == 0,
                NextValue(self.seed_req, 0),
                NextValue(seed_ctr, 16), # seed in 512 bits for DIN
                NextState("DIN_SEEDING"),
            ).Else(
                NextValue(self.seed_req, ~self.seed_gnt),
            )
        )
        seedfsm.act("DIN_SEEDING",
            If(seed_gnt_rising,
                din_shift.eq(1),
                NextValue(seed_ctr, seed_ctr - 1),
            ),
            If(seed_ctr == 0,
                NextValue(self.seed_req, 0),
                NextState("SEEDED"),
            ).Else(
                NextValue(self.seed_req, ~self.seed_gnt),
            )
        )
        seedfsm.act("SEEDED",
            If(ready,
                NextValue(init, 1),
                NextState("WAIT_INIT")
            )
        )
        seedfsm.act("WAIT_INIT",
            NextValue(selfmix_ctr, selfmix_interval_local),
            NextValue(init, 0),
            If(valid,
                NextState("RUN"),
                NextValue(self.ready, 1),
            )
        )
        seedfsm.act("RUN",
            If(selfmix_ena_local,
                If(selfmix_ctr != 0,
                    NextValue(selfmix_ctr, selfmix_ctr - 1),
                ).Else(
                    NextValue(selfmix_ctr, selfmix_interval_local),
                    force_round.eq(1),
                )
            ),
            If(advance_block,
                If(reseed_ctr < reseed_interval_local,
                    NextValue(reseed_ctr, reseed_ctr + 1),
                ),
                If((reseed_ctr == reseed_interval_local) & (reseed_interval_local != 0),
                    NextValue(reseed_ctr, 1),
                    NextValue(self.seed_req, 1),
                    NextState("RUN_RESEED"),
                )
            )
        )
        seedfsm.act("RUN_RESEED",
            If(seed_gnt_rising,
                state_rot.eq(1),
                NextValue(self.seed_req, 0),
                NextState("RUN"),
            )
        )
        # a simple FSM just to manage the ready/wait on the chacha block itself
        advfsm = FSM(reset_state="WAITING")
        self.submodules += advfsm
        advfsm.act("WAITING",
            NextValue(next, 0),
            If(advance_block,
                NextState("WAIT_READY")
            )
        )
        advfsm.act("WAIT_READY",
            If(ready,
                NextValue(next, 1),
                NextState("WAITING"),
            )
        )
        self.sync += holding_buf_load.eq(valid) # just load the buf whenever we see a new valid block come out

        # verilog block instantiation
        self.comb += [
            key.eq(state[:256]),
            iv.eq(state[256:320]),
            ctr.eq(state[320:]),
        ]
        # stretch control signal pulses out
        self.submodules.init_xfer = BlindTransfer("sys", "clk50")
        self.submodules.next_xfer = BlindTransfer("sys", "clk50")
        self.submodules.force_xfer = BlindTransfer("sys", "clk50")
        self.comb += [
            self.init_xfer.i.eq(init),
            self.next_xfer.i.eq(next),
            self.force_xfer.i.eq(force_round),
            init_50.eq(self.init_xfer.o),
            next_50.eq(self.next_xfer.o),
            force_round_50.eq(self.force_xfer.o),
        ]
        # make sure we have a solid local reset
        resetter = Signal(4, reset=15)
        local_reset_n = Signal(reset=0)
        self.sync.clk50 += [
            If(resetter != 0,
                resetter.eq(resetter - 1),
                    local_reset_n.eq(0),
            ).Else(
                If(ResetSignal("clk50"),
                    resetter.eq(15),
                    local_reset_n.eq(0),
                ).Else(
                    resetter.eq(0),
                    local_reset_n.eq(1),
                )
            )
        ]
        self.specials += Instance("chacha_core",
            i_clk = ClockSignal("clk50"),
            i_reset_n = local_reset_n,

            i_init = init_50,
            i_next = next_50,

            i_key = key,
            i_keylen = 1, # select a 256-bit keylen
            i_iv = iv,
            i_ctr = ctr,
            i_rounds = 20, # minimum of 20 rounds
            i_data_in = data_in,
            i_force_round = force_round_50,
            o_ready = ready_50,
            o_data_out = data_out_50,
            o_data_out_valid = valid_50,
        )

        platform.add_source(os.path.join("deps", "gateware", "gateware", "chacha", "chacha_core.v"))
        platform.add_source(os.path.join("deps", "gateware", "gateware", "chacha", "chacha_qr.v"))

        ### sys->clk50 multi-cycle paths:
        # relax the path from the data_in and key into the chacha engine. it's basically static.
        platform.add_platform_command("set_multicycle_path 2 -setup -start -from [get_clocks sys_clk] -to [get_clocks clk50] -through [get_pins chacha_core/data_out_reg_*/D]")
        platform.add_platform_command("set_multicycle_path 1 -hold -end -from [get_clocks sys_clk] -to [get_clocks clk50] -through [get_pins chacha_core/data_out_reg_*/D]")
        platform.add_platform_command("set_multicycle_path 2 -setup -start -from [get_clocks sys_clk] -to [get_clocks clk50] -through [get_pins chacha_core/state_reg*/D]")
        platform.add_platform_command("set_multicycle_path 1 -hold -end -from [get_clocks sys_clk] -to [get_clocks clk50] -through [get_pins chacha_core/state_reg*/D]")
        ### clk50->sys multi-cycle paths:
        # relax the return path, the valid pulse is pipelined so there are oodles of setup/hold time
        # setup is defined w.r.t destination clock, so the numbers are 2x larger in th clk50->sys direction
        platform.add_platform_command("set_multicycle_path 4 -setup -start -from [get_clocks clk50] -to [get_clocks sys_clk] -through [get_pins chacha_core/data_out_reg*/Q]")
        platform.add_platform_command("set_multicycle_path 2 -hold -end -from [get_clocks clk50] -to [get_clocks sys_clk] -through [get_pins chacha_core/data_out_reg*/Q]")
        platform.add_platform_command("set_false_path -through [get_pins *trngmanaged_data_out_reg*/D]") # use a big hammer because the timing analyzer is refusing to obey the less drastic measures below
Exemplo n.º 9
0
    def __init__(self, tsc, channels, lane_count=8, fifo_depth=128):
        self.cri = cri.Interface()
        self.reset = CSR()
        self.reset_phy = CSR()
        self.async_error = CSR(3)
        self.collision_channel = CSRStatus(16)
        self.busy_channel = CSRStatus(16)
        self.sequence_error_channel = CSRStatus(16)

        # Clocking/Reset
        # Create rsys, rio and rio_phy domains based on sys and rtio
        # with reset controlled by CSR.
        #
        # The `rio` CD contains logic that is reset with `core.reset()`.
        # That's state that could unduly affect subsequent experiments,
        # i.e. input overflows caused by input gates left open, FIFO events far
        # in the future blocking the experiment, pending RTIO or
        # wishbone bus transactions, etc.
        # The `rio_phy` CD contains state that is maintained across
        # `core.reset()`, i.e. TTL output state, OE, DDS state.
        cmd_reset = Signal(reset=1)
        cmd_reset_phy = Signal(reset=1)
        self.sync += [
            cmd_reset.eq(self.reset.re),
            cmd_reset_phy.eq(self.reset_phy.re)
        ]
        cmd_reset.attr.add("no_retiming")
        cmd_reset_phy.attr.add("no_retiming")

        self.clock_domains.cd_rsys = ClockDomain()
        self.clock_domains.cd_rio = ClockDomain()
        self.clock_domains.cd_rio_phy = ClockDomain()
        self.comb += [
            self.cd_rsys.clk.eq(ClockSignal()),
            self.cd_rsys.rst.eq(cmd_reset),
            self.cd_rio.clk.eq(ClockSignal("rtio")),
            self.cd_rio_phy.clk.eq(ClockSignal("rtio"))
        ]
        self.specials += AsyncResetSynchronizer(self.cd_rio, cmd_reset)
        self.specials += AsyncResetSynchronizer(self.cd_rio_phy, cmd_reset_phy)

        # TSC
        chan_fine_ts_width = max(
            max(
                rtlink.get_fine_ts_width(channel.interface.o)
                for channel in channels),
            max(
                rtlink.get_fine_ts_width(channel.interface.i)
                for channel in channels))
        assert tsc.glbl_fine_ts_width >= chan_fine_ts_width

        # Outputs/Inputs
        quash_channels = [
            n for n, c in enumerate(channels) if isinstance(c, LogChannel)
        ]

        outputs = SED(channels,
                      tsc.glbl_fine_ts_width,
                      "async",
                      quash_channels=quash_channels,
                      lane_count=lane_count,
                      fifo_depth=fifo_depth,
                      interface=self.cri)
        self.submodules += outputs
        self.comb += outputs.coarse_timestamp.eq(tsc.coarse_ts)
        self.sync += outputs.minimum_coarse_timestamp.eq(tsc.coarse_ts_sys +
                                                         16)

        inputs = InputCollector(tsc,
                                channels,
                                "async",
                                quash_channels=quash_channels,
                                interface=self.cri)
        self.submodules += inputs

        # Asychronous output errors
        o_collision_sync = BlindTransfer("rio", "rsys", data_width=16)
        o_busy_sync = BlindTransfer("rio", "rsys", data_width=16)
        self.submodules += o_collision_sync, o_busy_sync
        o_collision = Signal()
        o_busy = Signal()
        o_sequence_error = Signal()
        self.sync += [
            If(
                self.async_error.re,
                If(self.async_error.r[0], o_collision.eq(0)),
                If(self.async_error.r[1], o_busy.eq(0)),
                If(self.async_error.r[2], o_sequence_error.eq(0)),
            ),
            If(
                o_collision_sync.o, o_collision.eq(1),
                If(~o_collision,
                   self.collision_channel.status.eq(o_collision_sync.data_o))),
            If(o_busy_sync.o, o_busy.eq(1),
               If(~o_busy, self.busy_channel.status.eq(o_busy_sync.data_o))),
            If(
                outputs.sequence_error, o_sequence_error.eq(1),
                If(
                    ~o_sequence_error,
                    self.sequence_error_channel.status.eq(
                        outputs.sequence_error_channel)))
        ]
        self.comb += self.async_error.w.eq(
            Cat(o_collision, o_busy, o_sequence_error))

        self.comb += [
            o_collision_sync.i.eq(outputs.collision),
            o_collision_sync.data_i.eq(outputs.collision_channel),
            o_busy_sync.i.eq(outputs.busy),
            o_busy_sync.data_i.eq(outputs.busy_channel)
        ]
Exemplo n.º 10
0
    def __init__(self, platform):
        self.key_0_q = CSRStorage(fields=[
            CSRField(
                "key_0", size=32, description="least significant key word")
        ])
        self.key_1_q = CSRStorage(
            fields=[CSRField("key_1", size=32, description="key word 1")])
        self.key_2_q = CSRStorage(
            fields=[CSRField("key_2", size=32, description="key word 2")])
        self.key_3_q = CSRStorage(
            fields=[CSRField("key_3", size=32, description="key word 3")])
        self.key_4_q = CSRStorage(
            fields=[CSRField("key_4", size=32, description="key word 4")])
        self.key_5_q = CSRStorage(
            fields=[CSRField("key_5", size=32, description="key word 5")])
        self.key_6_q = CSRStorage(
            fields=[CSRField("key_6", size=32, description="key word 6")])
        self.key_7_q = CSRStorage(fields=[
            CSRField("key_7", size=32, description="most significant key word")
        ])

        self.dataout_0 = CSRStatus(fields=[
            CSRField("data_0", size=32, description="data output from cipher")
        ])
        self.dataout_1 = CSRStatus(fields=[
            CSRField("data_1", size=32, description="data output from cipher")
        ])
        self.dataout_2 = CSRStatus(fields=[
            CSRField("data_2", size=32, description="data output from cipher")
        ])
        self.dataout_3 = CSRStatus(fields=[
            CSRField("data_3", size=32, description="data output from cipher")
        ])

        self.datain_0 = CSRStorage(
            fields=[CSRField("data_0", size=32, description="data input")])
        self.datain_1 = CSRStorage(
            fields=[CSRField("data_1", size=32, description="data input")])
        self.datain_2 = CSRStorage(
            fields=[CSRField("data_2", size=32, description="data input")])
        self.datain_3 = CSRStorage(
            fields=[CSRField("data_3", size=32, description="data input")])

        self.iv_0 = CSRStorage(
            fields=[CSRField("iv_0", size=32, description="iv")])
        self.iv_1 = CSRStorage(
            fields=[CSRField("iv_1", size=32, description="iv")])
        self.iv_2 = CSRStorage(
            fields=[CSRField("iv_2", size=32, description="iv")])
        self.iv_3 = CSRStorage(
            fields=[CSRField("iv_3", size=32, description="iv")])

        self.ctrl = CSRStorage(fields=[
            CSRField("mode",
                     size=3,
                     description=
                     "set cipher mode. Illegal values mapped to `AES_ECB`",
                     values=[
                         ("001", "AES_ECB"),
                         ("010", "AES_CBC"),
                         ("100", "AES_CTR"),
                     ]),
            CSRField(
                "key_len",
                size=3,
                description=
                "length of the aes block. Illegal values mapped to `AES128`",
                values=[
                    ("001", "AES128"),
                    ("010", "AES192"),
                    ("100", "AES256"),
                ]),
            CSRField(
                "manual_operation",
                size=1,
                description=
                "If `1`, operation starts when `trigger` bit `start` is written, otherwise automatically on data and IV ready"
            ),
            CSRField(
                "operation",
                size=1,
                description=
                "Sets encrypt/decrypt operation. `0` = encrypt, `1` = decrypt"
            ),
        ])
        self.status = CSRStatus(fields=[
            CSRField("idle", size=1, description="Core idle", reset=1),
            CSRField("stall", size=1, description="Core stall"),
            CSRField("output_valid", size=1, description="Data output valid"),
            CSRField(
                "input_ready",
                size=1,
                description=
                "Input value has been latched and it is OK to update to a new value",
                reset=1),
            CSRField("operation_rbk", size=1,
                     description="Operation readback"),
            CSRField("mode_rbk",
                     size=3,
                     description="Actual mode selected by hardware readback"),
            CSRField("key_len_rbk",
                     size=3,
                     description=
                     "Actual key length selected by the hardware readback"),
            CSRField("manual_operation_rbk",
                     size=1,
                     description="Manual operation readback")
        ])
        status_idle = Signal()
        status_idle_de = Signal()
        status_stall = Signal()
        status_stall_de = Signal()
        output_valid = Signal()
        output_valid_de = Signal()
        input_ready = Signal()
        input_ready_de = Signal()
        self.sync += [
            If(status_idle_de, self.status.fields.idle.eq(status_idle)).Else(
                self.status.fields.idle.eq(self.status.fields.idle)),
            If(status_stall_de,
               self.status.fields.stall.eq(status_stall)).Else(
                   self.status.fields.stall.eq(self.status.fields.stall)),
            If(output_valid_de,
               self.status.fields.output_valid.eq(output_valid)).Else(
                   self.status.fields.output_valid.eq(
                       self.status.fields.output_valid)),
            If(input_ready_de,
               self.status.fields.input_ready.eq(input_ready)).Else(
                   self.status.fields.input_ready.eq(
                       self.status.fields.input_ready)),
        ]

        self.trigger = CSRStorage(fields=[
            CSRField("start",
                     size=1,
                     description=
                     "Triggers an AES computation if manual_start is selected",
                     pulse=True),
            CSRField(
                "key_clear", size=1, description="Clears the key", pulse=True),
            CSRField(
                "iv_clear", size=1, description="Clears the IV", pulse=True),
            CSRField("data_in_clear",
                     size=1,
                     description="Clears data input",
                     pulse=True),
            CSRField("data_out_clear",
                     size=1,
                     description="Clears the data output",
                     pulse=True),
            CSRField(
                "prng_reseed", size=1, description="Reseed PRNG", pulse=True),
        ])
        key0re50 = Signal()
        self.submodules.key0re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key0re.i.eq(self.key_0_q.re),
            key0re50.eq(self.key0re.o)
        ]
        key1re50 = Signal()
        self.submodules.key1re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key1re.i.eq(self.key_1_q.re),
            key1re50.eq(self.key1re.o)
        ]
        key2re50 = Signal()
        self.submodules.key2re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key2re.i.eq(self.key_2_q.re),
            key2re50.eq(self.key2re.o)
        ]
        key3re50 = Signal()
        self.submodules.key3re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key3re.i.eq(self.key_3_q.re),
            key3re50.eq(self.key3re.o)
        ]
        key4re50 = Signal()
        self.submodules.key4re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key4re.i.eq(self.key_4_q.re),
            key4re50.eq(self.key4re.o)
        ]
        key5re50 = Signal()
        self.submodules.key5re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key5re.i.eq(self.key_5_q.re),
            key5re50.eq(self.key5re.o)
        ]
        key6re50 = Signal()
        self.submodules.key6re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key6re.i.eq(self.key_6_q.re),
            key6re50.eq(self.key6re.o)
        ]
        key7re50 = Signal()
        self.submodules.key7re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.key7re.i.eq(self.key_7_q.re),
            key7re50.eq(self.key7re.o)
        ]

        iv0_50 = Signal()
        self.submodules.iv0_50 = BlindTransfer("sys", "clk50")
        self.comb += [self.iv0_50.i.eq(self.iv_0.re), iv0_50.eq(self.iv0_50.o)]
        iv1_50 = Signal()
        self.submodules.iv1_50 = BlindTransfer("sys", "clk50")
        self.comb += [self.iv1_50.i.eq(self.iv_1.re), iv1_50.eq(self.iv1_50.o)]
        iv2_50 = Signal()
        self.submodules.iv2_50 = BlindTransfer("sys", "clk50")
        self.comb += [self.iv2_50.i.eq(self.iv_2.re), iv2_50.eq(self.iv2_50.o)]
        iv3_50 = Signal()
        self.submodules.iv3_50 = BlindTransfer("sys", "clk50")
        self.comb += [self.iv3_50.i.eq(self.iv_3.re), iv3_50.eq(self.iv3_50.o)]

        ctrlre50 = Signal()
        self.submodules.ctrl50re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.ctrl50re.i.eq(self.ctrl.re),
            ctrlre50.eq(self.ctrl50re.o)
        ]

        di0_50 = Signal()
        self.submodules.di0_50 = BlindTransfer("sys", "clk50")
        self.comb += [
            self.di0_50.i.eq(self.datain_0.re),
            di0_50.eq(self.di0_50.o)
        ]
        di1_50 = Signal()
        self.submodules.di1_50 = BlindTransfer("sys", "clk50")
        self.comb += [
            self.di1_50.i.eq(self.datain_1.re),
            di1_50.eq(self.di1_50.o)
        ]
        di2_50 = Signal()
        self.submodules.di2_50 = BlindTransfer("sys", "clk50")
        self.comb += [
            self.di2_50.i.eq(self.datain_2.re),
            di2_50.eq(self.di2_50.o)
        ]
        di3_50 = Signal()
        self.submodules.di3_50 = BlindTransfer("sys", "clk50")
        self.comb += [
            self.di3_50.i.eq(self.datain_3.re),
            di3_50.eq(self.di3_50.o)
        ]

        trigger_start = Signal()
        self.submodules.trigstart = BlindTransfer("sys", "clk50")
        self.comb += [
            self.trigstart.i.eq(self.trigger.fields.start),
            trigger_start.eq(self.trigstart.o)
        ]
        trigger_key_clear = Signal()
        self.submodules.trigkeyclear = BlindTransfer("sys", "clk50")
        self.comb += [
            self.trigkeyclear.i.eq(self.trigger.fields.key_clear),
            trigger_key_clear.eq(self.trigkeyclear.o)
        ]
        trigger_iv_clear = Signal()
        self.submodules.trigivclear = BlindTransfer("sys", "clk50")
        self.comb += [
            self.trigivclear.i.eq(self.trigger.fields.iv_clear),
            trigger_iv_clear.eq(self.trigivclear.o)
        ]
        trigger_data_in_clear = Signal()
        self.submodules.trigdatinclear = BlindTransfer("sys", "clk50")
        self.comb += [
            self.trigdatinclear.i.eq(self.trigger.fields.data_in_clear),
            trigger_data_in_clear.eq(self.trigdatinclear.o)
        ]
        trigger_data_out_clear = Signal()
        self.submodules.trigdatoutclear = BlindTransfer("sys", "clk50")
        self.comb += [
            self.trigdatoutclear.i.eq(self.trigger.fields.data_out_clear),
            trigger_data_out_clear.eq(self.trigdatoutclear.o)
        ]
        trigger_prng_reseed = Signal()
        self.submodules.trigprng = BlindTransfer("sys", "clk50")
        self.comb += [
            self.trigprng.i.eq(self.trigger.fields.prng_reseed),
            trigger_prng_reseed.eq(self.trigprng.o)
        ]

        dataout0_re = Signal()
        self.submodules.dataout0_re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.dataout0_re.i.eq(self.dataout_0.we),
            dataout0_re.eq(self.dataout0_re.o)
        ]
        dataout1_re = Signal()
        self.submodules.dataout1_re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.dataout1_re.i.eq(self.dataout_1.we),
            dataout1_re.eq(self.dataout1_re.o)
        ]
        dataout2_re = Signal()
        self.submodules.dataout2_re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.dataout2_re.i.eq(self.dataout_2.we),
            dataout2_re.eq(self.dataout2_re.o)
        ]
        dataout3_re = Signal()
        self.submodules.dataout3_re = BlindTransfer("sys", "clk50")
        self.comb += [
            self.dataout3_re.i.eq(self.dataout_3.we),
            dataout3_re.eq(self.dataout3_re.o)
        ]
        self.specials += Instance(
            "aes_reg_top",
            i_clk_i=ClockSignal("clk50"),
            i_rst_ni=~ResetSignal("clk50"),
            i_key_0_q=self.key_0_q.fields.key_0,
            i_key_0_qe=key0re50,
            i_key_1_q=self.key_1_q.fields.key_1,
            i_key_1_qe=key1re50,
            i_key_2_q=self.key_2_q.fields.key_2,
            i_key_2_qe=key2re50,
            i_key_3_q=self.key_3_q.fields.key_3,
            i_key_3_qe=key3re50,
            i_key_4_q=self.key_4_q.fields.key_4,
            i_key_4_qe=key4re50,
            i_key_5_q=self.key_5_q.fields.key_5,
            i_key_5_qe=key5re50,
            i_key_6_q=self.key_6_q.fields.key_6,
            i_key_6_qe=key6re50,
            i_key_7_q=self.key_7_q.fields.key_7,
            i_key_7_qe=key7re50,
            o_data_out_0=self.dataout_0.fields.data_0,
            i_data_out_0_re=dataout0_re,
            o_data_out_1=self.dataout_1.fields.data_1,
            i_data_out_1_re=dataout1_re,
            o_data_out_2=self.dataout_2.fields.data_2,
            i_data_out_2_re=dataout2_re,
            o_data_out_3=self.dataout_3.fields.data_3,
            i_data_out_3_re=dataout3_re,
            i_iv_0_q=self.iv_0.fields.iv_0,
            i_iv_0_qe=iv0_50,
            i_iv_1_q=self.iv_1.fields.iv_1,
            i_iv_1_qe=iv1_50,
            i_iv_2_q=self.iv_2.fields.iv_2,
            i_iv_2_qe=iv2_50,
            i_iv_3_q=self.iv_3.fields.iv_3,
            i_iv_3_qe=iv3_50,
            i_data_in_0=self.datain_0.fields.data_0,
            i_data_in_1=self.datain_1.fields.data_1,
            i_data_in_2=self.datain_2.fields.data_2,
            i_data_in_3=self.datain_3.fields.data_3,
            i_data_in_0_qe=di0_50,
            i_data_in_1_qe=di1_50,
            i_data_in_2_qe=di2_50,
            i_data_in_3_qe=di3_50,
            i_ctrl_mode=self.ctrl.fields.mode,
            i_ctrl_key_len=self.ctrl.fields.key_len,
            i_ctrl_manual_operation=self.ctrl.fields.manual_operation,
            i_ctrl_operation=self.ctrl.fields.operation,
            i_ctrl_update=ctrlre50,
            o_idle=status_idle,
            o_idle_de=status_idle_de,
            o_stall=status_stall,
            o_stall_de=status_stall_de,
            o_output_valid=output_valid,
            o_output_valid_de=output_valid_de,
            o_input_ready=input_ready,
            o_input_ready_de=input_ready_de,
            o_ctrl_key_len_rbk=self.status.fields.key_len_rbk,
            o_operation_rbk=self.status.fields.operation_rbk,
            o_mode_rbk=self.status.fields.mode_rbk,
            o_manual_operation_rbk=self.status.fields.manual_operation_rbk,
            i_start=trigger_start,
            i_key_clear=trigger_key_clear,
            i_iv_clear=trigger_iv_clear,
            i_data_in_clear=trigger_data_in_clear,
            i_data_out_clear=trigger_data_out_clear,
            i_prng_reseed=trigger_prng_reseed,
        )
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "prim", "rtl",
                         "prim_assert.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_reg_pkg.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_pkg.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_control.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_key_expand.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_mix_columns.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_mix_single_column.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_sbox_canright.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_sbox_lut.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_sbox.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_shift_rows.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_sub_bytes.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_core.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_ctr.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_cipher_core.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_cipher_control.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "prim", "rtl",
                         "prim_cipher_pkg.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "prim", "rtl",
                         "prim_lfsr.sv"))
        platform.add_source(
            os.path.join("deps", "opentitan", "hw", "ip", "aes", "rtl",
                         "aes_prng.sv"))
        platform.add_source(
            os.path.join("deps", "gateware", "gateware", "aes_reg_litex.sv"))
Exemplo n.º 11
0
    def __init__(self, platform):
        default_period = 325000000
        self.intro = ModuleDoc("""Watch Dog Timer
A watchdog timer for Betrusted. Once enabled, it cannot be disabled, and
it must have the reset_wdt bit written periodically to avoid a watchdog reset event.

If this does not happen, the WDT will attempt to toggle reset of the full chip via GSR.

The timeout period is specified in 'approximately 65MHz' (15.38ns) periods by the period register.
According to Xilinx, 'approximately' is +/-50%.

The register cannot be updated once the WDT is running.
        """)
        self.gsr = Signal()  # wire to the GSR line for the FPGA
        self.cfgmclk = Signal(
        )  # wire to FPGA's CFGMCLK ring oscillator, a "typical" 65MHz clock
        self.clock_domains.cd_wdt = ClockDomain()
        self.specials += Instance("BUFG",
                                  i_I=self.cfgmclk,
                                  o_O=self.cd_wdt.clk)
        platform.add_platform_command(
            "create_clock -name wdt -period 10.256 [get_nets {net}]",
            net=self.cd_wdt.clk)  # 65MHz + 50% tolerance

        ### WATCHDOG RESET, uses the extcomm_div divider to save on gates
        self.watchdog = CSRStorage(fields=[
            CSRField("reset_wdt",
                     size=1,
                     description=
                     "Write to this register to reset the watchdog timer",
                     pulse=True),
            CSRField(
                "enable",
                description=
                "Enable the watchdog timer. Cannot be disabled once enabled, except with a reset. Notably, a watchdog reset will disable the watchdog.",
                reset=0),
        ])
        reset_wdt = Signal()
        w_stretch = Signal(3)
        reset_wdt_sys = Signal()
        self.sync += [
            If(self.watchdog.fields.reset_wdt,
               w_stretch.eq(7)).Elif(w_stretch > 0,
                                     w_stretch.eq(w_stretch - 1)),
            reset_wdt_sys.eq(w_stretch != 0),
        ]
        self.submodules.reset_sync = BlindTransfer("sys", "wdt")
        self.comb += [
            self.reset_sync.i.eq(reset_wdt_sys),
            reset_wdt.eq(self.reset_sync.o),
        ]
        self.period = CSRStorage(
            32,
            fields=[
                CSRField(
                    "period",
                    size=32,
                    description=
                    "Number of 'approximately 65MHz' CFGMCLK cycles before each reset_code must be entered. Defaults to a range of {:0.2f}-{:0.2f} seconds"
                    .format((default_period * 10.256e-9) * 0.5,
                            (default_period * 10.256e-9) * 1.5),
                    reset=default_period)
            ])

        self.state = CSRStatus(
            4,
            fields=[
                CSRField("enabled", size=1,
                         description="WDT has been enabled"),
                CSRField("armed1",
                         size=1,
                         description="WDT in the armed1 state"),
                CSRField("armed2",
                         size=1,
                         description="WDT in the armed2 state"),
                CSRField("disarmed",
                         size=1,
                         description="WDT in the disarmed state"),
            ])
        armed1 = Signal()
        armed2 = Signal()
        disarmed = Signal()
        self.specials += MultiReg(armed1, self.state.fields.armed1)
        self.specials += MultiReg(armed2, self.state.fields.armed2)
        self.specials += MultiReg(disarmed, self.state.fields.disarmed)

        wdog_enable_wdt = Signal()
        self.specials += MultiReg(self.watchdog.fields.enable,
                                  wdog_enable_wdt,
                                  odomain="wdt")
        wdog_enabled = Signal(reset=0)
        wdog_enabled_r = Signal(reset=0)
        self.sync.wdt += [
            If(wdog_enable_wdt,
               wdog_enabled.eq(1)).Else(wdog_enabled.eq(wdog_enabled)),
            wdog_enabled_r.eq(wdog_enabled)
        ]
        self.specials += MultiReg(wdog_enabled, self.state.fields.enabled)

        self.submodules.period_sync = BusSynchronizer(32, "sys", "wdt")
        wdt_count = Signal(32)
        self.comb += [
            self.period_sync.i.eq(self.period.fields.period),
            wdt_count.eq(self.period_sync.o)
        ]
        wdt_count_lock = Signal(32)
        wdt_start = Signal()
        self.sync.wdt += [
            wdt_start.eq(~wdog_enabled_r & wdog_enabled),
            If(~wdog_enabled_r & wdog_enabled,
               wdt_count_lock.eq(wdt_count)).Else(
                   wdt_count_lock.eq(wdt_count_lock), )
        ]
        wdog_counter = Signal(32, reset=default_period)
        wdog_cycle = Signal()
        self.sync.wdt += [
            If(
                wdt_start,
                wdog_counter.eq(wdt_count_lock),
            ).Else(
                If(
                    wdog_enabled,
                    If(wdog_counter == 0, wdog_cycle.eq(1),
                       wdog_counter.eq(wdt_count_lock)).Else(
                           wdog_counter.eq(wdog_counter - 1),
                           wdog_cycle.eq(0),
                       )))
        ]
        do_reset = Signal()
        wdog = ClockDomainsRenamer("wdt")(FSM(reset_state="IDLE"))
        self.submodules += wdog
        wdog.act("IDLE", If(wdog_enabled, NextState("DISARMED")))
        wdog.act(
            "ARMED_HOT",
            armed2.eq(1),
            If(reset_wdt, NextState("DISARMED")).Elif(
                wdog_cycle,
                do_reset.eq(1),
            ),
        )
        # double-interlock: not having responded to the watchdog code immediately
        # is not cause for a reset: this could just be the wdog_cycle hitting at an
        # inopportune time relative to the watchdog reset routine.
        # instead, escalate to the ARMED_HOT state so that
        # the watchdog next period, if no action was taken, we do a reset
        wdog.act(
            "ARMED", armed1.eq(1),
            If(reset_wdt, NextState("DISARMED")).Elif(wdog_cycle,
                                                      NextState("ARMED_HOT")))
        wdog.act("DISARMED", disarmed.eq(1), If(wdog_cycle,
                                                NextState("ARMED")))
        do_reset_sys = Signal()
        reset_stretch = Signal(5, reset=0)
        self.submodules.do_res_sync = BlindTransfer("wdt", "sys")
        self.comb += [
            self.do_res_sync.i.eq(do_reset),
            do_reset_sys.eq(self.do_res_sync.o),
        ]
        self.sync += [
            If(
                do_reset_sys,
                reset_stretch.eq(31),
                self.gsr.eq(0),
            ).Elif(
                reset_stretch != 0,
                self.gsr.eq(1),
                reset_stretch.eq(reset_stretch - 1),
            ).Else(
                reset_stretch.eq(0),
                self.gsr.eq(0),
            )
        ]
Exemplo n.º 12
0
    def __init__(self, clkspertick, clkfreq, bits=64):
        self.clkspertick = int(clkfreq / clkspertick)

        self.intro = ModuleDoc("""TickTimer: A practical systick timer.

        TIMER0 in the system gives a high-resolution, sysclk-speed timer which overflows
        very quickly and requires OS overhead to convert it into a practically usable time source
        which counts off in systicks, instead of sysclks.

        The hardware parameter to the block is the divisor of sysclk, and sysclk. So if
        the divisor is 1000, then the increment for a tick is 1ms. If the divisor is 2000,
        the increment for a tick is 0.5ms. 
        
        Note to self: substantial area savings could be hand by being smarter about the
        synchronization between the always-on and the TickTimer domains. Right now about 1.8%
        of the chip is eaten up by ~1100 synchronization registers to cross the 64-bit values
        between the clock domains. Since the values move rarely, a slightly smarter method
        would be to create a lock-out around a read pulse and then create some false_path
        rules around the datapaths to keep the place/route from getting distracted by the
        cross-domain clocks.
        """)

        resolution_in_ms = 1000 * (self.clkspertick / clkfreq)
        self.note = ModuleDoc(
            title="Configuration",
            body=
            "This timer was configured with {} bits, which rolls over in {:.2f} years, with each bit giving {}ms resolution"
            .format(bits, (2**bits / (60 * 60 * 24 * 365)) *
                    (self.clkspertick / clkfreq), resolution_in_ms))

        prescaler = Signal(max=self.clkspertick, reset=self.clkspertick)
        timer = Signal(bits)

        # cross-process domain signals. Broken out to a different CSR so it can be on a different virtual memory page.
        self.pause = Signal()
        pause = Signal()
        self.specials += MultiReg(self.pause, pause, "always_on")

        self.load = Signal()
        self.submodules.load_xfer = BlindTransfer("sys", "always_on")
        self.comb += self.load_xfer.i.eq(self.load)

        self.paused = Signal()
        paused = Signal()
        self.specials += MultiReg(paused, self.paused)

        self.timer = Signal(bits)
        self.submodules.timer_sync = BusSynchronizer(bits, "always_on", "sys")
        self.comb += [
            self.timer_sync.i.eq(timer),
            self.timer.eq(self.timer_sync.o)
        ]
        self.resume_time = Signal(bits)
        self.submodules.resume_sync = BusSynchronizer(bits, "sys", "always_on")
        self.comb += [self.resume_sync.i.eq(self.resume_time)]

        self.control = CSRStorage(fields=[
            CSRField(
                "reset",
                description=
                "Write a `1` to this bit to reset the count to 0. This bit has priority over all other requests.",
                pulse=True),
        ])
        self.time = CSRStatus(bits,
                              name="time",
                              description="""Elapsed time in systicks""")
        self.comb += self.time.status.eq(self.timer_sync.o)

        self.submodules.reset_xfer = BlindTransfer("sys", "always_on")
        self.comb += [
            self.reset_xfer.i.eq(self.control.fields.reset),
        ]

        self.sync.always_on += [
            If(
                self.reset_xfer.o,
                timer.eq(0),
                prescaler.eq(self.clkspertick),
            ).Elif(
                self.load_xfer.o,
                prescaler.eq(self.clkspertick),
                timer.eq(self.resume_sync.o),
            ).Else(
                If(
                    prescaler == 0, prescaler.eq(self.clkspertick),
                    If(pause == 0, timer.eq(timer + 1),
                       paused.eq(0)).Else(timer.eq(timer), paused.eq(1))).Else(
                           prescaler.eq(prescaler - 1), ))
        ]

        self.msleep = ModuleDoc("""msleep extension
        
        The msleep extension is a Xous-specific add-on to aid the implementation of the msleep server.
        
        msleep fires an interrupt when the requested time is less than or equal to the current elapsed time in
        systicks. The interrupt remains active until a new target is set, or masked. 
        
        There is a slight slip in time (~200ns) from when the msleep timer is set before it can take effect.
        This is because it takes many CPU clock cycles to transfer this data into the always-on clock
        domain, which runs at a much slower rate than the CPU clock.
        """)
        self.msleep_target = CSRStorage(
            size=bits,
            description="Target time in {}ms ticks".format(resolution_in_ms))
        self.submodules.ev = EventManager()
        self.ev.alarm = EventSourceLevel()
        # sys-domain alarm is computed using sys-domain time view, so that the trigger condition
        # corresponds tightly to the setting of the target time
        alarm_trigger = Signal()
        self.comb += self.ev.alarm.trigger.eq(alarm_trigger)

        # always_on domain gets a delayed copy of msleep_target
        # thus its output may not match that of the sys-domain alarm
        # in particular, it takes time for msleep_target update to propagate through
        # the bus synchronizers; however, the "trigger" enable for the system is handled
        # in the sys-domain, and can be set *before* the bus synchronizers have passed the
        # data through. This causes the alarm to glitch prematurely.

        # if we seem to be errantly aborting WFI's that are entered shortly after
        # setting an msleep target, this race condition is likely the culprit.

        # the circuit below locks out alarms for the duration of time that it takes for
        # msleep_target to propagate to its target, and back again
        self.submodules.ping = BlindTransfer("sys", "always_on")
        self.comb += self.ping.i.eq(self.msleep_target.re)
        self.submodules.pong = BlindTransfer("always_on", "sys")
        self.comb += self.pong.i.eq(self.ping.o)
        lockout_alarm = Signal()
        self.comb += [
            If(lockout_alarm, alarm_trigger.eq(0)).Else(
                alarm_trigger.eq(
                    self.msleep_target.storage <= self.timer_sync.o))
        ]
        self.sync += [
            If(self.msleep_target.re, lockout_alarm.eq(1)).Elif(
                self.pong.o,
                lockout_alarm.eq(0)).Else(lockout_alarm.eq(lockout_alarm))
        ]

        # re-compute the alarm signal in the "always on" domain -- so that this can trigger even when the CPU clock is stopped
        alarm = Signal()
        self.submodules.target_xfer = BusSynchronizer(bits, "sys", "always_on")
        self.comb += self.target_xfer.i.eq(self.msleep_target.storage)
        self.sync.always_on += alarm.eq(self.target_xfer.o <= timer)

        self.alarm_always_on = Signal()
        self.comb += self.alarm_always_on.eq(alarm)
Exemplo n.º 13
0
    def __init__(self, tsc, channels, mode, quash_channels=[], interface=None):
        if interface is None:
            interface = cri.Interface()
        self.cri = interface

        # # #

        if mode == "sync":
            fifo_factory = SyncFIFOBuffered
            sync_io = self.sync
            sync_cri = self.sync
        elif mode == "async":
            fifo_factory = lambda *args: ClockDomainsRenamer({
                "write": "rio",
                "read": "rsys"
            })(AsyncFIFO(*args))
            sync_io = self.sync.rio
            sync_cri = self.sync.rsys
        else:
            raise ValueError

        i_statuses, i_datas, i_timestamps = [], [], []
        i_ack = Signal()
        sel = self.cri.chan_sel[:16]
        for n, channel in enumerate(channels):
            iif = channel.interface.i
            if iif is None or n in quash_channels:
                i_datas.append(0)
                i_timestamps.append(0)
                i_statuses.append(0)
                continue

            # FIFO
            layout = get_channel_layout(len(tsc.coarse_ts), iif)
            fifo = fifo_factory(layout_len(layout), channel.ififo_depth)
            self.submodules += fifo
            fifo_in = Record(layout)
            fifo_out = Record(layout)
            self.comb += [
                fifo.din.eq(fifo_in.raw_bits()),
                fifo_out.raw_bits().eq(fifo.dout)
            ]

            # FIFO write
            if iif.delay:
                counter_rtio = Signal.like(tsc.coarse_ts, reset_less=True)
                sync_io += counter_rtio.eq(tsc.coarse_ts - (iif.delay + 1))
            else:
                counter_rtio = tsc.coarse_ts
            if hasattr(fifo_in, "data"):
                self.comb += fifo_in.data.eq(iif.data)
            if hasattr(fifo_in, "timestamp"):
                if hasattr(iif, "fine_ts"):
                    full_ts = Cat(iif.fine_ts, counter_rtio)
                else:
                    full_ts = counter_rtio
                self.comb += fifo_in.timestamp.eq(full_ts)
            self.comb += fifo.we.eq(iif.stb)

            overflow_io = Signal()
            self.comb += overflow_io.eq(fifo.we & ~fifo.writable)
            if mode == "sync":
                overflow_trigger = overflow_io
            elif mode == "async":
                overflow_transfer = BlindTransfer("rio", "rsys")
                self.submodules += overflow_transfer
                self.comb += overflow_transfer.i.eq(overflow_io)
                overflow_trigger = overflow_transfer.o
            else:
                raise ValueError

            # FIFO read, CRI connection
            if hasattr(fifo_out, "data"):
                i_datas.append(fifo_out.data)
            else:
                i_datas.append(0)
            if hasattr(fifo_out, "timestamp"):
                ts_shift = 64 - len(fifo_out.timestamp)
                i_timestamps.append(fifo_out.timestamp << ts_shift)
            else:
                i_timestamps.append(0)

            selected = Signal()
            self.comb += selected.eq(sel == n)

            overflow = Signal()
            sync_cri += [
                If(selected & i_ack, overflow.eq(0)),
                If(overflow_trigger, overflow.eq(1))
            ]
            self.comb += fifo.re.eq(selected & i_ack & ~overflow)
            i_statuses.append(Cat(fifo.readable & ~overflow, overflow))

        i_status_raw = Signal(2)
        self.comb += i_status_raw.eq(Array(i_statuses)[sel])
        input_timeout = Signal.like(self.cri.i_timeout, reset_less=True)
        input_pending = Signal()
        self.cri.i_data.reset_less = True
        self.cri.i_timestamp.reset_less = True
        sync_cri += [
            i_ack.eq(0),
            If(
                i_ack,
                self.cri.i_status.eq(Cat(~i_status_raw[0], i_status_raw[1],
                                         0)),
                self.cri.i_data.eq(Array(i_datas)[sel]),
                self.cri.i_timestamp.eq(Array(i_timestamps)[sel]),
            ),
            If((tsc.full_ts_cri >= input_timeout) | (i_status_raw != 0),
               If(input_pending, i_ack.eq(1)), input_pending.eq(0)),
            If(self.cri.cmd == cri.commands["read"],
               input_timeout.eq(self.cri.i_timeout), input_pending.eq(1),
               self.cri.i_status.eq(0b100))
        ]