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) ]
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))
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)
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)) ]
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), ]
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"))
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"))
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
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) ]
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"))
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), ) ]
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)
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)) ]