def add_csrs(self): self._start = CSR() # CSR does not take a description parameter so we must set it manually self._start.description = "Writing to this register initializes payload execution" self._status = CSRStatus(fields=[ CSRField("ready", description="Indicates that the executor is not running"), CSRField( "overflow", description="Indicates the scratchpad memory address counter" " has overflown due to the number of READ commands sent during execution" ), ], description="Payload executor status register" ) self._read_count = CSRStatus( len(self.scratchpad.counter), description="Number of data" " from READ commands that is stored in the scratchpad memory") self.comb += [ self.start.eq(self._start.re), self._status.fields.ready.eq(self.ready), self._status.fields.overflow.eq(self.scratchpad.overflow), self._read_count.status.eq(self.scratchpad.counter), ]
def add_csrs(self): super().add_csrs() self._error_count = CSRStatus(size=len(self.error_count), description='Number of errors detected') self._skip_fifo = CSRStorage( description='Skip waiting for user to read the errors FIFO') self._error_offset = CSRStatus( size=len(self.mem_mask), description='Current offset of the error') self._error_data = CSRStatus( size=len(self.data_port.dat_r), description='Erroneous value read from DRAM memory') self._error_expected = CSRStatus( size=len(self.data_port.dat_r), description='Value expected to be read from DRAM memory') self._error_ready = CSRStatus( description='Error detected and ready to read') self._error_continue = CSR() self._error_continue.description = 'Continue reading until the next error' self.comb += [ self._error_count.status.eq(self.error_count), self.skip_fifo.eq(self._skip_fifo.storage), self._error_offset.status.eq(self.error.offset), self._error_data.status.eq(self.error.data), self._error_expected.status.eq(self.error.expected), self.error.ready.eq(self._error_continue.re), self._error_ready.status.eq(self.error.valid), ]
def __init__(self): self.submodules.fifo = f = fifo.SyncFIFOBuffered(width=8, depth=64) in_reg = CSRStorage(8, name="in", description=""" Write half of the FIFO to send data out the Messible. Writing to this register advances the write pointer automatically.""" ) out_reg = CSRStatus(8, name="out", description=""" Read half of the FIFO to receive data on the Messible. Reading from this register advances the read pointer automatically.""" ) self.__setattr__("in", in_reg) self.__setattr__("out", out_reg) self.status = status = CSRStatus(fields=[ CSRField( "full", description="``0`` if more data can fit into the IN FIFO."), CSRField( "have", description="``1`` if data can be read from the OUT FIFO."), ]) self.intro = ModuleDoc(""" Messible: An Ansible for Messages An Ansible is a system for instant communication across vast distances, from a small portable device to a huge terminal far away. A Messible is a message- passing system from embedded devices to a host system. You can use it to get very simple printf()-style support over a debug channel. The Messible assumes the host has a way to peek into the device's memory space. This is the case with the Wishbone bridge, which allows both the device and the host to access the same memory. At its core, a Messible is a FIFO. As long as the ``STATUS.FULL`` bit is ``0``, the device can write data into the Messible by writing into the ``IN``. However, if this value is ``1``, you need to decide if you want to wait for it to empty (if the other side is just slow), or if you want to drop the message. From the host side, you need to read ``STATUS.HAVE`` to see if there is data in the FIFO. If there is, read ``OUT`` to get the most recent byte, which automatically advances the ``READ`` pointer. """) self.comb += [ f.din.eq(in_reg.storage), f.we.eq(in_reg.re), out_reg.status.eq(f.dout), f.re.eq(out_reg.we), status.fields.full.eq(~f.writable), status.fields.have.eq(f.readable), ]
def __init__(self, size=None, *fields, name=None, reset=0, description=None): """Create a memory-mapped DCSRStatus. Depending on the value of `readable` and `writeable`, Reg will be backed by either a :obj:`CSRStorage` or :obj:`CSRStatus`. It will then create convenience wrappers around this core storage. It is possible to create registers that are both `readable` and `writeable`, in which case convenience methods will be prefixed with `i_` and `o_`. Args: size (int) (optional): How wide to make this register A width may be specified. If none is specified, then the width is set to the total size of all `fields`. If no fields are specified, then the width must be specified. name (:obj:`str`): The name of register. The name should be all lowercase, and be a valid Python identifier. The name must not start with "_". This will be transformed into all-caps for certain operations. If no name is specified, :obj:`Reg` will attempt to infer it, but may raise an error if it can't figure out what to call itself. reset (:obj:`Signal(n)`): Value of the :obj:`CSRStatus` right after reset. The :obj:`CSRStatus` will immediately take its value after the first cycle, however it can be useful to provide an initialization value here for simulation purposes. description (:obj:`str`): An overview of this register This field contains Markdown data that describes this register's overall contents. *fields (:obj:`list` of :obj:`Field`): All fields of this register. Each entry in the list is a single :obj:`Field`. The order of the fields represents the order in which they will be added to the :obj:`Reg`. It is possible to have disjoint fields by setting the `offset` parameter, however it is an error to have overlapping :obj:`Field` regions. If there are no fields, then a single Field is created and the width must be specified. """ (size, fields) = get_size_and_fields(size, fields, "status") bits = get_bit_list(fields) try: CSRStatus.__init__(self, len(bits), reset=reset, name=name) except Exception as e: raise ValueError( "Cannot extract CSRStatus name from code -- please provide one by passing `name=` to the initializer: {}" .format(e)) self.make_status_signals(bits)
def __init__(self, k2mm: K2MM, dw=32): self.source_ctrl = Endpoint(k2mm.sink_tester_ctrl.description) self._probe_len = CSRStorage(description="Test frame length", fields=[ CSRField( "length", size=16, description="Test frame length"), ], name="prb_length") self._probe_ctrl = CSRStorage( description="Test frame enable", fields=[CSRField("enable", size=1, description="Send test frame")], name="prb_ctrl") self._probe_status = CSRStatus( description="Probe status", fields=[ CSRField("ready", size=1, description="1 = Test frame command ready"), ], name="prb_stat") self.comb += [ self.source_ctrl.length.eq(self._probe_len.fields.length), self._probe_status.fields.ready.eq(self.source_ctrl.ready), self.source_ctrl.valid.eq(self._probe_ctrl.fields.enable & self._probe_ctrl.re) ]
def add_csrs(self): self._refresh_count = CSRStatus( len(self.refresh_counter.counter), description= "Count of all refresh commands issued (both by Memory Controller and Payload Executor)." " Value is latched from internal counter on mode trasition: MC -> PE or by writing to" " the `refresh_update` CSR.") self._at_refresh = CSRStorage( len(self.at_refresh), reset=0, description= "If set to a value different than 0 the mode transition MC -> PE will be peformed only" " when the value of this register matches the current refresh commands count." ) self._refresh_update = CSR() self._refresh_update.description = "Force an update of the `refresh_count` CSR." self.comb += self.at_refresh.eq(self._at_refresh.storage) # detect mode transition pe_ongoing = self.fsm.ongoing("PAYLOAD-EXECUTION") mc_ongoing = self.fsm.ongoing("MEMORY-CONTROLLER") mc_ongoing_d = Signal() self.sync += mc_ongoing_d.eq(mc_ongoing) mc_to_pe = mc_ongoing_d & pe_ongoing self.sync += If( mc_to_pe | self._refresh_update.re, self._refresh_count.status.eq(self.refresh_counter.counter), )
def __init__(self, pads): touch1 = TSTriple() touch2 = TSTriple() touch3 = TSTriple() touch4 = TSTriple() self.specials += touch1.get_tristate(pads.t1) self.specials += touch2.get_tristate(pads.t2) self.specials += touch3.get_tristate(pads.t3) self.specials += touch4.get_tristate(pads.t4) self.o = CSRStorage(size=4) self.oe = CSRStorage(size=4) self.i = CSRStatus(size=4) self.comb += [ touch1.o.eq(self.o.storage[0]), touch2.o.eq(self.o.storage[1]), touch3.o.eq(self.o.storage[2]), touch4.o.eq(self.o.storage[3]), touch1.oe.eq(self.oe.storage[0]), touch2.oe.eq(self.oe.storage[1]), touch3.oe.eq(self.oe.storage[2]), touch4.oe.eq(self.oe.storage[3]), self.i.status.eq(Cat(touch1.i, touch2.i, touch3.i, touch4.i)) ]
def __init__( self, pwm_pad, pwm_dev=None, clk_period=1e9/16e6, resolution=1e3, max_width=2000e3, csr_width=8, alignment=2, ): if pwm_dev == None: self.submodules.pwm_dev = ppm.PWMinput( clk_period=clk_period, resolution=resolution, max_width=max_width, ) else: self.pwm_dev = pwm_dev # assign an input pin to the PPM device self.comb += self.pwm_dev.pwm.eq(pwm_pad) self.width = CSRStatus(csr_width, name='width') pwm_size = self.pwm_dev.width.nbits if csr_width >= pwm_size: self.comb += self.width.status[:pwm_size].eq(self.pwm_dev.width) else: self.comb += self.width.status.eq(self.pwm_dev.width[alignment:csr_width+alignment])
def __init__(self, sys_clk_freq, pins): self.value = CSRStatus(8) self.submodules.encoder = _RotaryEncoder(sys_clk_freq) self.comb += [self.encoder.i_a.eq(pins.a), self.encoder.i_b.eq(pins.b)] self.sync += self.value.status.eq(self.encoder.o_counter)
def add_csr(self): csr_helper(self, 'channel', self.channel, cdc=True) csr_helper(self, 'threshold', self.threshold, cdc=True) csr_helper(self, 'wait_pre', self.wait_pre, cdc=True) csr_helper(self, 'wait_acq', self.wait_acq, cdc=True) csr_helper(self, 'wait_post', self.wait_post, cdc=True) self.trig_count_csr = CSRStatus(32, name='trig_count') self.specials += MultiReg(self.trig_count, self.trig_count_csr.status)
def add_csrs(self): self._start = CSR() self._start.description = 'Write to the register starts the transfer (if ready=1)' self._ready = CSRStatus( description='Indicates that the transfer is not ongoing') self._count = CSRStorage(size=len(self.count), description='Desired number of DMA transfers') self._done = CSRStatus(size=len(self.done), description='Number of completed DMA transfers') self._mem_mask = CSRStorage( size=len(self.mem_mask), description='DRAM address mask for DMA transfers') self._data_mask = CSRStorage(size=len(self.mem_mask), description='Pattern memory address mask') self.comb += [ self.start.eq(self._start.re), self._ready.status.eq(self.ready), self.count.eq(self._count.storage), self._done.status.eq(self.done), self.mem_mask.eq(self._mem_mask.storage), self.data_mask.eq(self._data_mask.storage), ]
def __init__(self): SimSoC.__init__(self, cpu_type=None) self.platform.add_source("I2C_slave.v") # For making sure csr registers are read back correctly self.status_test = CSRStatus(8) self.comb += self.status_test.status.eq(0xDE) # For testing bit-banging I2C self.submodules.i2c_master = m = I2CMaster() self.add_csr('i2c_master') # Hardwire SDA line to High!!! self.comb += m.pads.sda.eq(1)
def __init__(self, pads): self.alt_fields = ["csr_control"] pins = [0, 1, 5, 6, 9, 10, 11, 12, 13, 18, 19, 20, 21] nbits = len(pins) fields = [] for n in pins: fields += [ CSRField(f"io{n}", 1, n, description=f"Control for I/O pin {n}"), ] self._oe = CSRStorage(nbits, description="""GPIO Tristate(s) Control. Write ``1`` enable output driver""", fields=fields) self._in = CSRStatus(nbits, description="""GPIO Input(s) Status. Input value of IO pad as read by the FPGA""", fields=fields) self._out = CSRStorage(nbits, description="""GPIO Ouptut(s) Control. Value loaded into the output driver""", fields=fields) # # # self._io = [] for n, p in zip(pins, pads): m = IOPin(p) # Create a connection to the CSR alt_csr = io_pins() self.comb += [ alt_csr.o.eq(self._out.storage[n]), alt_csr.oe.eq(self._oe.storage[n]), self._in.status.eq(alt_csr.i) ] m.add_alt(alt_csr) self.submodules += m self._io += [(n, m)]
def __init__( self, ppm_pad, servo_pads=None, ppm_dev=None, channels=8, timeout=4000e3, clk_period=1e9/16e6, resolution=1e3, max_width=2000e3, csr_width=8, alignment=2, ): # instantiate the device if it isn't provided if ppm_dev == None: self.submodules.ppm_dev = ppm.PPMinput( channels=channels, timeout=timeout, clk_period=clk_period, resolution=resolution, max_width=max_width, ) else: self.ppm_dev = ppm_dev channels = self.ppm_dev.channels # assign an input pin to the PPM device self.comb += self.ppm_dev.ppm.eq(ppm_pad) self.widths = [] for chan in range(channels): tmp = CSRStatus(csr_width, name='channel_'+str(chan)) self.widths.append(tmp) setattr(self, f"tmp{chan}", tmp) for chan in range(channels): ppm_size = self.ppm_dev.widths[chan].nbits if csr_width >= ppm_size: self.comb += self.widths[chan].status[:ppm_size].eq(self.ppm_dev.widths[chan]) else: self.comb += self.widths[chan].status.eq(self.ppm_dev.widths[chan][alignment:csr_width+alignment]) if servo_pads != None: assert(len(servo_pads) == channels) self.comb += servo_pads[chan].eq(self.ppm_dev.pwm[chan])
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): self.wishbone = wishbone.Interface() self._start = CSRStorage(fields=[CSRField("start_burst", size=1, offset=0, pulse=True)]) self._ready = CSRStatus(8) self._burst_size = CSRStorage(16) self._base = CSRStorage(32) self._offset = CSRStorage(32) words_count = Signal(16) pass_count = Signal(5) self.wb_dma = wb_dma = WishboneDMAWriter(self.wishbone, endianness="big") self.submodules += wb_dma self.comb += [ self.wb_dma.sink.address.eq((self._base.storage >> 2) + (self._offset.storage>>2) + words_count), self.wb_dma.sink.data.eq(pass_count), ] fsm = FSM(reset_state="WAIT-FOR-TRIGGER") self.submodules += fsm fsm.act("WAIT-FOR-TRIGGER", self._ready.status.eq(1), NextValue(words_count, 0), If(self._start.fields.start_burst, NextState("WRITE-DATA"), ) ) fsm.act("WRITE-DATA", self.wb_dma.sink.valid.eq(1), If(self.wb_dma.sink.ready, NextValue(words_count, words_count+1), If(words_count == (self._burst_size.storage-1), NextState("WAIT-FOR-TRIGGER"), NextValue(pass_count, pass_count+1) ) ) )
def __init__(self, width, depth, port, **kwargs): self.adc_data = adc_data = Signal(width) acq_start, acq_start_x = Signal(reset=0), Signal(reset=0) self._buf_full = CSRStatus( fields=[CSRField("acq_complete", size=1, offset=0)]) self._acq_start = CSRStorage( fields=[CSRField("acq_start", size=1, offset=0, pulse=True)]) w_addr = Signal(16, reset=0) self.comb += [ self._buf_full.fields.acq_complete.eq(w_addr == depth), acq_start.eq(self._acq_start.fields.acq_start), port.adr.eq(w_addr), port.dat_w.eq(adc_data), port.we.eq(w_addr != depth) ] self.submodules.ps = PulseSynchronizer("sys", "sample") self.comb += [self.ps.i.eq(acq_start), acq_start_x.eq(self.ps.o)] self.sync.sample += [ If(acq_start_x & (w_addr == depth), w_addr.eq(0)).Elif(w_addr != depth, w_addr.eq(w_addr + 1)) ]
def __init__(self, dma): address_width = len(dma.sink.address) self.enabled = CSRStorage( description="Used to start/stop the operation of the module") self.address1 = CSRStorage(address_width, description="First attacked address") self.address2 = CSRStorage(address_width, description="Second attacked address") self.count = CSRStatus( 32, description="""This is the number of DMA accesses performed. When the module is enabled, the value can be freely read. When the module is disabled, the register is clear-on-write and has to be read before the next attack.""") counter = Signal.like(self.count.status) self.comb += self.count.status.eq(counter) self.sync += \ If(self.enabled.storage, If(dma.sink.valid & dma.sink.ready, counter.eq(counter + 1) ) ).Elif(self.count.we, # clear on read when not enabled counter.eq(0) ) address = Signal(address_width) self.comb += Case( counter[0], { 0: address.eq(self.address1.storage), 1: address.eq(self.address2.storage), }) self.comb += [ dma.sink.address.eq(address), dma.sink.valid.eq(self.enabled.storage), dma.source.ready.eq(1), ]
def __init__(self, platform, pads): self.reset = Signal() mosi_pad = TSTriple() miso_pad = TSTriple() cs_n_pad = TSTriple() clk_pad = TSTriple() wp_pad = TSTriple() hold_pad = TSTriple() self.do = CSRStorage(size=6) self.oe = CSRStorage(size=6) self.di = CSRStatus(size=6) self.specials += mosi_pad.get_tristate(pads.mosi) self.specials += miso_pad.get_tristate(pads.miso) self.specials += cs_n_pad.get_tristate(pads.cs_n) self.specials += clk_pad.get_tristate(pads.clk) self.specials += wp_pad.get_tristate(pads.wp) self.specials += hold_pad.get_tristate(pads.hold) self.comb += [ mosi_pad.oe.eq(self.oe.storage[0]), miso_pad.oe.eq(self.oe.storage[1]), wp_pad.oe.eq(self.oe.storage[2]), hold_pad.oe.eq(self.oe.storage[3]), clk_pad.oe.eq(self.oe.storage[4]), cs_n_pad.oe.eq(self.oe.storage[5]), mosi_pad.o.eq(self.do.storage[0]), miso_pad.o.eq(self.do.storage[1]), wp_pad.o.eq(self.do.storage[2]), hold_pad.o.eq(self.do.storage[3]), clk_pad.o.eq(self.do.storage[4]), cs_n_pad.o.eq(self.do.storage[5]), self.di.status.eq( Cat(mosi_pad.i, miso_pad.i, wp_pad.i, hold_pad.i, clk_pad.i, cs_n_pad.i)), ]
def __init__(self, pads): _fields_input = [ CSRField("MODPRSL"), CSRField("INTL"), ] _fields_output = [ CSRField("MODSELL", reset=1), CSRField("RESETL", reset=1), CSRField("LPMODE", reset=0), ] self.status = status = CSRStatus(fields=_fields_input, name="qsfp0_status") self.control = control = CSRStorage(fields=_fields_output, name="qsfp0_control") for _f in _fields_input: self.comb += getattr(status.fields, _f.name).eq(getattr(pads, _f.name)) for _f in _fields_output: self.comb += getattr(pads, _f.name).eq(getattr(control.fields, _f.name))
def __init__(self, pads, gpio_name): nbits = len(pads) fields = [CSRField(fld[0], description=fld[1]) for fld in gpio_name] self._oe = CSRStorage(nbits, description="GPIO Tristate(s) Control.", fields=fields) self._in = CSRStatus(nbits, description="GPIO Input(s) Status.", fields=fields) self._out = CSRStorage(nbits, description="GPIO Output(s) Control.", fields=fields) # # # _pads = Signal(nbits) self.comb += _pads.eq(pads) for i in range(nbits): t = TSTriple() self.specials += t.get_tristate(_pads[i]) self.comb += t.oe.eq(self._oe.storage[i]) self.comb += t.o.eq(self._out.storage[i]) self.specials += MultiReg(t.i, self._in.status[i])
def __init__(self, pads): self.intro = ModuleDoc("""Fomu Touchpads Fomu has four single-ended exposed pads on its side. These pads are designed to be connected to some captouch block, or driven in a resistive touch mode in order to get simple touchpad support. This block simply provides CPU-controlled GPIO support for this block. It has three registers which control the In, Out, and Output Enable functionality of each of these pins. """) touch1 = TSTriple() touch2 = TSTriple() touch3 = TSTriple() touch4 = TSTriple() self.specials += touch1.get_tristate(pads.t1) self.specials += touch2.get_tristate(pads.t2) self.specials += touch3.get_tristate(pads.t3) self.specials += touch4.get_tristate(pads.t4) self.o = CSRStorage(4, description="Output values for pads 1-4") self.oe = CSRStorage(4, description="Output enable control for pads 1-4") self.i = CSRStatus(4, description="Input value for pads 1-4") self.comb += [ touch1.o.eq(self.o.storage[0]), touch2.o.eq(self.o.storage[1]), touch3.o.eq(self.o.storage[2]), touch4.o.eq(self.o.storage[3]), touch1.oe.eq(self.oe.storage[0]), touch2.oe.eq(self.oe.storage[1]), touch3.oe.eq(self.oe.storage[2]), touch4.oe.eq(self.oe.storage[3]), self.i.status.eq(Cat(touch1.i, touch2.i, touch3.i, touch4.i)) ]
def __init__(self, platform, pads, size=16 * 1024 * 1024): self.size = size self.bus = bus = wishbone.Interface() self.reset = Signal() self.cfg1 = CSRStorage(fields=[ CSRField( "bb_out", size=4, description="Output bits in bit-bang mode"), CSRField("bb_clk", description="Serial clock line in bit-bang mode"), CSRField("bb_cs", description="Chip select line in bit-bang mode"), ]) self.cfg2 = CSRStorage(fields=[ CSRField("bb_oe", size=4, description="Output Enable bits in bit-bang mode"), ]) self.cfg3 = CSRStorage(fields=[ CSRField( "rlat", size=4, description="Read latency/dummy cycle count"), CSRField("crm", description="Continuous Read Mode enable bit"), CSRField("qspi", description="Quad-SPI enable bit"), CSRField("ddr", description="Double Data Rate enable bit"), ]) self.cfg4 = CSRStorage(fields=[ CSRField( "memio", offset=7, reset=1, description= "Enable memory-mapped mode (set to 0 to enable bit-bang mode)") ]) self.stat1 = CSRStatus(fields=[ CSRField( "bb_in", size=4, description="Input bits in bit-bang mode"), ]) self.stat2 = CSRStatus(8, description="Reserved") self.stat3 = CSRStatus(8, description="Reserved") self.stat4 = CSRStatus(8, description="Reserved") cfg = Signal(32) cfg_we = Signal(4) cfg_out = Signal(32) self.comb += [ cfg.eq( Cat(self.cfg1.storage, self.cfg2.storage, self.cfg3.storage, self.cfg4.storage)), cfg_we.eq( Cat(self.cfg1.re, self.cfg2.re, self.cfg3.re, self.cfg4.re)), self.stat1.status.eq(cfg_out[0:4]), self.stat2.status.eq(0), self.stat3.status.eq(0), self.stat4.status.eq(0), ] reset = Signal() mosi_pad = TSTriple() miso_pad = TSTriple() cs_n_pad = TSTriple() clk_pad = TSTriple() wp_pad = TSTriple() hold_pad = TSTriple() clk = Signal() if hasattr(pads, "clk"): clk = pads.clk self.specials += clk_pad.get_tristate(clk) self.comb += clk_pad.oe.eq(~reset) else: self.specials += Instance("USRMCLK", i_USRMCLKI=clk, i_USRMCLKTS=0) self.specials += mosi_pad.get_tristate(pads.mosi) self.specials += miso_pad.get_tristate(pads.miso) self.specials += cs_n_pad.get_tristate(pads.cs_n) self.specials += wp_pad.get_tristate(pads.wp) self.specials += hold_pad.get_tristate(pads.hold) self.comb += [ reset.eq(ResetSignal() | self.reset), cs_n_pad.oe.eq(~reset), ] flash_addr = Signal(24) # size/4 because data bus is 32 bits wide, -1 for base 0 mem_bits = bits_for(int(size / 4) - 1) pad = Signal(2) self.comb += flash_addr.eq(Cat(pad, bus.adr[0:mem_bits - 1])) read_active = Signal() spi_ready = Signal() self.sync += [ If( bus.stb & bus.cyc & ~read_active, read_active.eq(1), bus.ack.eq(0), ).Elif( read_active & spi_ready, read_active.eq(0), bus.ack.eq(1), ).Else( bus.ack.eq(0), read_active.eq(0), ) ] o_rdata = Signal(32) self.comb += bus.dat_r.eq(o_rdata) self.specials += Instance( "spimemio", o_flash_io0_oe=mosi_pad.oe, o_flash_io1_oe=miso_pad.oe, o_flash_io2_oe=wp_pad.oe, o_flash_io3_oe=hold_pad.oe, o_flash_io0_do=mosi_pad.o, o_flash_io1_do=miso_pad.o, o_flash_io2_do=wp_pad.o, o_flash_io3_do=hold_pad.o, o_flash_csb=cs_n_pad.o, o_flash_clk=clk, i_flash_io0_di=mosi_pad.i, i_flash_io1_di=miso_pad.i, i_flash_io2_di=wp_pad.i, i_flash_io3_di=hold_pad.i, i_resetn=~reset, i_clk=ClockSignal(), i_valid=bus.stb & bus.cyc, o_ready=spi_ready, i_addr=flash_addr, o_rdata=o_rdata, i_cfgreg_we=cfg_we, i_cfgreg_di=cfg, o_cfgreg_do=cfg_out, ) platform.add_source("rtl/spimemio.v")
def add_csr(self): self.f_ref_csr = CSRStatus(32, name='f_meas') self.specials += MultiReg(self.n_zc, self.f_ref_csr.status)
def __init__(self, model, models, seed): """Create a `Version` block. Arguments: model (int): The value of the :obj:`CSRStatus(model)` field. models: a list() of (value, "shortname", "description") tuples. """ self.intro = ModuleDoc("""SoC Version Information This block contains various information about the state of the source code repository when this SoC was built. """) def makeint(i, base=10): try: return int(i, base=base) except: return 0 def get_gitver(): def decode_version(v): version = v.split(".") major = 0 minor = 0 rev = 0 if len(version) >= 3: rev = makeint(version[2]) if len(version) >= 2: minor = makeint(version[1]) if len(version) >= 1: major = makeint(version[0]) return (major, minor, rev) git_rev_cmd = subprocess.Popen( ["git", "describe", "--tags", "--dirty=+"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (git_stdout, _) = git_rev_cmd.communicate() if git_rev_cmd.wait() != 0: print('unable to get git version') return raw_git_rev = git_stdout.decode().strip() dirty = 0 if raw_git_rev[-1] == "+": raw_git_rev = raw_git_rev[:-1] dirty = 1 parts = raw_git_rev.split("-") major = 0 minor = 0 rev = 0 gitrev = 0 gitextra = 0 if len(parts) >= 3: if parts[0].startswith("v"): version = parts[0] if version.startswith("v"): version = parts[0][1:] (major, minor, rev) = decode_version(version) gitextra = makeint(parts[1]) if parts[2].startswith("g"): gitrev = makeint(parts[2][1:], base=16) elif len(parts) >= 2: if parts[1].startswith("g"): gitrev = makeint(parts[1][1:], base=16) version = parts[0] if version.startswith("v"): version = parts[0][1:] (major, minor, rev) = decode_version(version) elif len(parts) >= 1: version = parts[0] if version.startswith("v"): version = parts[0][1:] (major, minor, rev) = decode_version(version) return (major, minor, rev, gitrev, gitextra, dirty) (major, minor, rev, gitrev, gitextra, dirty) = get_gitver() self.major = CSRStatus( 8, reset=major, description= "Major git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``." .format(major, minor, rev, major)) self.minor = CSRStatus( 8, reset=minor, description= "Minor git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``." .format(major, minor, rev, minor)) self.revision = CSRStatus( 8, reset=rev, description= "Revision git tag version. For example, this firmware was built from git tag ``v{}.{}.{}``, so this value is ``{}``." .format(major, minor, rev, rev)) self.gitrev = CSRStatus( 32, reset=gitrev, description= "First 32-bits of the git revision. This documentation was built from git rev ``{:08x}``, so this value is {}, which should be enough to check out the exact git version used to build this firmware." .format(gitrev, gitrev)) self.gitextra = CSRStatus( 10, reset=gitextra, description= "The number of additional commits beyond the git tag. For example, if this value is ``1``, then the repository this was built from has one additional commit beyond the tag indicated in `MAJOR`, `MINOR`, and `REVISION`." ) self.dirty = CSRStatus(fields=[ CSRField( "dirty", reset=dirty, description= "Set to ``1`` if this device was built from a git repo with uncommitted modifications." ) ]) model_val = None for model_check in models: try: if int(model_check[0]) == int(model): model_val = int(model_check[0]) except: pass try: if model_check[1] == model: model_val = int(model_check[0]) except: pass if model_val is None: raise ValueError("Model not found in `models` list!") self.model = CSRStatus(fields=[ CSRField( "model", reset=model_val, size=8, description= "Contains information on which model device this was built for.", values=models) ]) self.seed = CSRStatus( 32, reset=seed, description="32-bit seed used for the place-and-route.") self.comb += [ self.major.status.eq(major), self.minor.status.eq(minor), self.revision.status.eq(rev), self.gitrev.status.eq(gitrev), self.gitextra.status.eq(gitextra), self.dirty.fields.dirty.eq(dirty), self.model.fields.model.eq(model_val), self.seed.status.eq(seed), ]
def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big"): """ Simple SPI flash. Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast Read). Only supports mode0 (cpol=0, cpha=0). """ self.bus = bus = wishbone.Interface() spi_width = len(pads.dq) assert spi_width >= 2 if with_bitbang: self.bitbang = CSRStorage(4) self.miso = CSRStatus() self.bitbang_en = CSRStorage() # # # cs_n = Signal(reset=1) clk = Signal() dq_oe = Signal() wbone_width = len(bus.dat_r) read_cmd_params = { 4: (_format_cmd(_QIOFR, 4), 4 * 8), 2: (_format_cmd(_DIOFR, 2), 2 * 8), } read_cmd, cmd_width = read_cmd_params[spi_width] addr_width = 24 dq = TSTriple(spi_width) self.specials.dq = dq.get_tristate(pads.dq) sr = Signal(max(cmd_width, addr_width, wbone_width)) if endianness == "big": self.comb += bus.dat_r.eq(sr) else: self.comb += bus.dat_r.eq(reverse_bytes(sr)) hw_read_logic = [ pads.clk.eq(clk), pads.cs_n.eq(cs_n), dq.o.eq(sr[-spi_width:]), dq.oe.eq(dq_oe) ] if with_bitbang: bitbang_logic = [ pads.clk.eq(self.bitbang.storage[1]), pads.cs_n.eq(self.bitbang.storage[2]), # In Dual/Quad mode, no single data pin is consistently # an input or output thanks to dual/quad reads, so we need a bit # to swap direction of the pins. Aside from this additional bit, # bitbang mode is identical for Single/Dual/Quad; dq[0] is mosi # and dq[1] is miso, meaning remaining data pin values don't # appear in CSR registers. If(self.bitbang.storage[3], dq.oe.eq(0)).Else(dq.oe.eq(1)), If( self.bitbang. storage[1], # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only. self.miso.status.eq(dq.i[1])), dq.o.eq( Cat(self.bitbang.storage[0], Replicate(1, spi_width - 1))) ] self.comb += [ If(self.bitbang_en.storage, bitbang_logic).Else(hw_read_logic) ] else: self.comb += hw_read_logic if div < 2: raise ValueError( "Unsupported value \'{}\' for div parameter for SpiFlash core". format(div)) else: i = Signal(max=div) dqi = Signal(spi_width) self.sync += [ If( i == div // 2 - 1, clk.eq(1), dqi.eq(dq.i), ), If(i == div - 1, i.eq(0), clk.eq(0), sr.eq(Cat(dqi, sr[:-spi_width]))).Else(i.eq(i + 1), ), ] # spi is byte-addressed, prefix by zeros z = Replicate(0, log2_int(wbone_width // 8)) seq = [ (cmd_width // spi_width * div, [dq_oe.eq(1), cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]), (addr_width // spi_width * div, [sr[-addr_width:].eq(Cat(z, bus.adr))]), ((dummy + wbone_width // spi_width) * div, [dq_oe.eq(0)]), (1, [bus.ack.eq(1), cs_n.eq(1)]), ( div, # tSHSL! [bus.ack.eq(0)]), (0, []), ] # accumulate timeline deltas t, tseq = 0, [] for dt, a in seq: tseq.append((t, a)) t += dt self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq)
def __init__(self, pads, dummy=15, div=2, with_bitbang=True, endianness="big"): """ Simple SPI flash. Supports 1-bit reads. Only supports mode0 (cpol=0, cpha=0). """ self.bus = bus = wishbone.Interface() if with_bitbang: self.bitbang = CSRStorage(4) self.miso = CSRStatus() self.bitbang_en = CSRStorage() # # # if hasattr(pads, "wp"): self.comb += pads.wp.eq(1) if hasattr(pads, "hold"): self.comb += pads.hold.eq(1) cs_n = Signal(reset=1) clk = Signal() wbone_width = len(bus.dat_r) read_cmd = _FAST_READ cmd_width = 8 addr_width = 24 sr = Signal(max(cmd_width, addr_width, wbone_width)) if endianness == "big": self.comb += bus.dat_r.eq(sr) else: self.comb += bus.dat_r.eq(reverse_bytes(sr)) hw_read_logic = [ pads.clk.eq(clk), pads.cs_n.eq(cs_n), pads.mosi.eq(sr[-1:]) ] if with_bitbang: bitbang_logic = [ pads.clk.eq(self.bitbang.storage[1]), pads.cs_n.eq(self.bitbang.storage[2]), If( self.bitbang. storage[1], # CPOL=0/CPHA=0 or CPOL=1/CPHA=1 only. self.miso.status.eq(pads.miso)), pads.mosi.eq(self.bitbang.storage[0]) ] self.comb += [ If(self.bitbang_en.storage, bitbang_logic).Else(hw_read_logic) ] else: self.comb += hw_read_logic if div < 2: raise ValueError( "Unsupported value \'{}\' for div parameter for SpiFlash core". format(div)) else: i = Signal(max=div) miso = Signal() self.sync += [ If( i == div // 2 - 1, clk.eq(1), miso.eq(pads.miso), ), If(i == div - 1, i.eq(0), clk.eq(0), sr.eq(Cat(miso, sr[:-1]))).Else(i.eq(i + 1), ), ] # spi is byte-addressed, prefix by zeros z = Replicate(0, log2_int(wbone_width // 8)) seq = [ (cmd_width * div, [cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]), (addr_width * div, [sr[-addr_width:].eq(Cat(z, bus.adr))]), ((dummy + wbone_width) * div, []), (1, [bus.ack.eq(1), cs_n.eq(1)]), ( div, # tSHSL! [bus.ack.eq(0)]), (0, []), ] # accumulate timeline deltas t, tseq = 0, [] for dt, a in seq: tseq.append((t, a)) t += dt self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq)
def __init__(self, pads): # All MIPI commands and data are 8-bits, with the exception of commands # to read and write memory to the LCD. # self.intro = ModuleDoc(title="LCDIF", body=""" """) self.cmd = CSRStorage( 8, description="Send an 8-bit command to the LCD panel") self.data = CSRStorage( 8, description="Send an 8-bit data byte to the LCD panel") self.ctrl = CSRStorage( 6, fields=[ CSRField("bl", description="Control the backlight state"), CSRField( "reset", reset=1, description="Directly connected to the LCD ``reset`` line" ), CSRField( "cs", reset=1, description="Directly connected to the LCD ``cs`` line"), CSRField( "fbstart", description="Switch to line renderer on the next VBL"), CSRField("fbena", description="Enable input to the line renderer"), CSRField( "read", description="Set to ``1`` to read data from the device", pulse=True), ]) self.status = CSRStatus(2, fields=[ CSRField( "fmark", description="Start-of-frame marker"), CSRField("id", description="LCD ID pin"), ]) self.fb = CSRStorage(1, fields=[CSRField("startcmd", description="???")]) self.response = CSRStorage( 8, description="Response data after reading from the device.") self.tpg = CSRStorage(1, description="Test pattern generators", fields=[ CSRField( "bluething", description="Kinda pretty blue display"), CSRField("rainbow", description="RGB pattern test"), ]) self.submodules.wheel = Wheel() idx_counter = Signal(16) # Data pins d = TSTriple(len(pads.db)) self.specials.d = d.get_tristate(pads.db) # D/CX (If 1, this is a data packet, otherwise it's a command packet) dcx = Signal() # RDX is 1 if this is a `read` packet (i.e. device-to-FPGA) rdx = Signal() # WRX is 1 if this is a `write` packet (i.e. device-to-FPGA) wrx = Signal() # TE is 1 on start-of-frame (?) te = Signal() self.comb += [ self.status.fields.fmark.eq(pads.fmark), self.status.fields.id.eq(pads.id), pads.cs.eq(self.ctrl.fields.cs), pads.rst.eq(~self.ctrl.fields.reset), pads.blen.eq(self.ctrl.fields.bl), pads.rs.eq(dcx), pads.wr.eq(wrx), pads.rd.eq(rdx), te.eq(pads.fmark), ] self.submodules.fsm = fsm = FSM() fsm.act( "IDLE", wrx.eq(1), rdx.eq(1), If( self.cmd.re, wrx.eq(0), d.o.eq(self.cmd.storage), d.oe.eq(1), NextState("SEND_CMD"), ).Elif( self.data.re, wrx.eq(0), dcx.eq(1), d.o.eq(self.data.storage), d.oe.eq(1), NextState("SEND_DATA"), ).Elif( self.ctrl.fields.read, rdx.eq(0), dcx.eq(1), d.oe.eq(0), NextState("READ_DATA"), ).Elif( self.tpg.fields.rainbow, wrx.eq(0), d.o.eq(0x2c), d.oe.eq(1), NextValue(idx_counter, 1), NextState("START_SEND_RAINBOW"), ).Elif( self.tpg.fields.bluething, wrx.eq(0), d.o.eq(0x2c), d.oe.eq(1), NextValue(idx_counter, 1), NextState("START_SEND_BLUETHING"), )) fsm.act( "SEND_DATA", dcx.eq(1), wrx.eq(1), rdx.eq(1), d.o.eq(self.data.storage), d.oe.eq(1), NextState("IDLE"), ) fsm.act( "SEND_CMD", dcx.eq(0), wrx.eq(1), rdx.eq(1), d.o.eq(self.cmd.storage), d.oe.eq(1), NextState("IDLE"), ) fsm.act( "READ_DATA", dcx.eq(1), wrx.eq(1), rdx.eq(1), d.oe.eq(0), NextValue(self.response.storage, d.i), NextState("IDLE"), ) offset_counter = Signal(16) x = Signal(9) y = Signal(9) fsm.act("START_SEND_RAINBOW", NextValue(x, 0), NextValue(y, 0), wrx.eq(1), rdx.eq(1), dcx.eq(1), d.o.eq(0x2c), d.oe.eq(1), NextState("SEND_RAINBOW")) fsm.act( "SEND_RAINBOW", dcx.eq(1), rdx.eq(1), wrx.eq(idx_counter[0]), NextValue(idx_counter, idx_counter + 1), # If the bottom bit is set, then increment X and/or Y. # We do it this way because of how the LCD encoding works. If( idx_counter[0], If(x < 479, NextValue(x, x + 1)).Else( NextValue(x, 0), If( y < 319, NextValue(y, y + 1), ).Else(NextValue(y, 0), )), ), # Assign the actual pixel data If(y < 90, d.o.eq(Cat(x[0:5], Signal(6), Signal(5)))).Elif( y < 180, d.o.eq(Cat(Signal(5), x[0:6], Signal(6)))).Else( d.o.eq(Cat(Signal(5), Signal(6), x[0:5]))), d.oe.eq(1), If( ~self.tpg.fields.rainbow, NextState("IDLE"), ), ) fsm.act("START_SEND_BLUETHING", wrx.eq(1), rdx.eq(1), dcx.eq(1), d.o.eq(0x2c), d.oe.eq(1), NextState("SEND_BLUETHING")) fsm.act( "SEND_BLUETHING", dcx.eq(1), rdx.eq(1), wrx.eq(idx_counter[0]), self.wheel.pos.eq(idx_counter[6:] + offset_counter[15:]), If(idx_counter > 320 * 2, NextValue(idx_counter, 0)).Else( NextValue(idx_counter, idx_counter + 1 + pads.fmark), ), NextValue(offset_counter, offset_counter + 1), d.oe.eq(1), d.o.eq(Cat(self.wheel.r[0:6], self.wheel.g[0:6], self.wheel.b[0:6])), If( ~self.tpg.fields.bluething, NextState("IDLE"), ), )
def __init__(self, platform, mem, minimal=False): self.intro = ModuleDoc("""FOMU Apple II+ A virtual computer within a virtual computer inside a USB port. Instantiate 6502 processor with 1MHz core clock. Tie in to system memory as a wishbone master. Create virtual keyboard, video display and disk drive. """) addr = Signal(16) dout = Signal(8) din = Signal(8) wren = Signal() iosel = Signal() ior_addr = Signal(8) r_memsel = Signal() w_memsel = Signal() clk_en = Signal() # Clock divider limits CPU activity active = Signal() # CPU is active this cycle available = Signal() # Key press ready for reading div1M = 4 idlecount = Signal(div1M) # Counter to slow CPU clock # Disk II Controller Registers disk_phase = Signal(4) disk_motor = Signal() disk_drive = Signal() #disk_write = Signal() disk_reading = Signal() # DOS trying to read sector disk_data_available = Signal() # Data available for DOS to read disk_data_wanted = Signal() # Data wanted by DOS disk_read = Signal() # Data read by DOS so clear readable simulation = getenv("SIMULATION") synthesis = not simulation # Wishbone visible registers self.control = CSRStorage(fields=[ CSRField( "Reset", reset=1 if synthesis else 0, # auto-start in sim description="6502 Reset line - 1: Reset Asserted, 0: Running"), CSRField("RWROM", size=1, description="Allow writes to ROM"), #CSRField("Pause", description="Halt processor allowing stepping"), #CSRField("Step", description="Single step 6502 one clock cycle"), #CSRField("NMI", size=1, description="Non-maskable interrupt"), #CSRField("IRQ", size=1, description="Maskable interrupt request"), #CSRField("RDY", size=1, description=""), CSRField( "Divisor", size=div1M, offset=8, reset=11 if synthesis else 0, # Over-clock simulation description="Clock divider minius 1: 11 for 1MHz, 0 for 12MHz" ), #CSRField("DI", size=8, offset=16, description=""), ]) self.keyboard = CSRStorage(8, write_from_dev=True, description="Keyboard input ($C000)") self.strobe = CSR(1) #, description="Keyboard strobe ($C010)") self.screen = CSRStatus( fields=[ CSRField("Character", size=8, description="Character written to screen"), CSRField("Valid", size=1, description="Character is valid"), CSRField( "More", size=1, description="Additional characters are available to read"), #CSRField("Move", size=1, # description="Character is not adjacent to previous"), CSRField("Repeat", size=1, offset=11, description= "Previous character repeated to current position"), CSRField("ScrollStart", size=1, offset=12, description="Start of scroll region detected"), CSRField("ScrollEnd", size=1, offset=13, description="End of scroll region"), CSRField( "Horizontal", size=6, offset=16, description="Location of current character in screen memory" ), CSRField( "Vertical", size=5, offset=24, description="Location of current character in screen memory" ), ], description="Video Display Output") self.diskctrl = CSRStatus( fields=[ CSRField("Phase", size=4, description= "Four phases of the track selection stepper motor"), CSRField("Motor", size=1, description="Drive is spinning"), CSRField( "Drive", size=1, description="Drive select: drive 1 if clear, drive 2 if set" ), CSRField("Wanted", size=1, description="Drive is waiting for data"), CSRField("Pending", size=1, description="Drive has not yet read data written"), #CSRField("WriteMode", size=1, # description="Drive is reading when clear, writing when set"), ], description="Disk drive control ($C0EX)") self.diskdata = CSRStorage(8, description="Disk drive data ($C0EC)") #self.bus=CSRStatus(32, fields=[ # CSRField("Addr", size=16, description="Address bus"), # CSRField("Data", size=8, description="Data bus"), # CSRField("WrEn", size=1, description="Write enable"), # ], description="Address and data bus") #self.debug=CSRStatus(32, fields=[ # CSRField("PC", size=8, # description="Program counter"), # CSRField("A", size=8, # description="Accumulator"), # CSRField("X", size=8, # description="X index register"), # CSRField("Y", size=8, # description="Y index register"), # ], description="Address and data bus") if not minimal: # The minimal configuration provides all registers the host needs to # see so software can run unmodified. However, it does not implement # the 6502 to save gates. The video driver is also greatly reduced. # TODO eliminate wire [31:0] apple2_display_fifo_wrport_dat_r self.submodules.display_fifo = fifo.SyncFIFOBuffered(width=32, depth=256) self.comb += [ mem.addr6502.eq(addr), mem.din6502.eq(dout), mem.wren6502.eq(wren), self.strobe.w.eq(available), #self.bus.fields.Addr.eq(addr), #self.bus.fields.Data.eq(dout), #self.bus.fields.WrEn.eq(wren), If(addr[8:16] == 0xC0, iosel.eq(1), w_memsel.eq(0), mem.access6502.eq(0)).Else(iosel.eq(0), w_memsel.eq(1), mem.access6502.eq(active)), disk_read.eq(0), If( r_memsel, din.eq(mem.dout6502), ).Else( # I/O Read Address Decoder (reading but not from memory) If( ior_addr[4:8] == 0x0, din.eq(Cat(self.keyboard.storage[0:7], available)), ), # Disk II Controller Card in slot 6 (0x8 | 0x6) # The only data to be read are locations C and D. Simplify the # logic to only look at bit 0 of the address. If( ior_addr[4:8] == 0xE, din[0:7].eq(self.diskdata.storage[0:7]), # Write protect - TODO write is not supported #If(ior_addr[0], # # Return high bit set - protected # din[7].eq(1) #).Else( disk_read.eq(1), If( disk_data_available | self.diskdata.re, # Return byte given by host din[7].eq(self.diskdata.storage[7]), ).Else( # Return high bit clear - data not available din[7].eq(0), ), #), ), ), active.eq(clk_en & self.display_fifo.writable), ] self.sync += [ # Slow clock to prototypical speed or as configured by user If( idlecount == self.control.fields.Divisor, idlecount.eq(0), clk_en.eq(1), ).Else( idlecount.eq(idlecount + 1), clk_en.eq(0), ), # Read (DI) follows Write (AB/DO) by one clock cycle If( active, r_memsel.eq(w_memsel), ior_addr.eq(addr[0:8]), ), # Keyboard key available when written by host If( self.keyboard.re, available.eq(1), ), If( iosel, # I/O Write Address Decoder If( addr[4:8] == 0x1, # KBDSTRB # Strobe cleared on read or write to KBDSTRB # Any read or write to this address clears the pending key available.eq(0), ), ), ] self.specials += Instance( "cpu", i_clk=ClockSignal(), i_reset=ResetSignal() | self.control.storage[0], i_DI=din, # &(self.rand.we|self.cfg.storage[1])), o_AB=addr, # .dat_w, o_DO=dout, # .dat_w, o_WE=wren, i_IRQ=False, # self.control.fields.IRQ, i_NMI=False, # self.control.fields.NMI, # seed.re, i_RDY=active, # self.control.fields.RDY, ) platform.add_source("rtl/verilog-6502/cpu.v") platform.add_source("rtl/verilog-6502/ALU.v") #=============================================================================== # Disk Drive - Emulate Disk II controller in slot 6 #=============================================================================== # The Disk II controller card has 16 addressable locations. Eight of these are # dedicated to moving the arm, two for motor control, two for drive selection # and four that handle read, write, and write protection detection. #=============================================================================== self.comb += [ self.diskctrl.fields.Phase.eq(disk_phase), self.diskctrl.fields.Motor.eq(disk_motor), self.diskctrl.fields.Drive.eq(disk_drive), #self.diskctrl.fields.WriteMode.eq(disk_write), self.diskctrl.fields.Wanted.eq(disk_data_wanted), self.diskctrl.fields.Pending.eq(disk_data_available), ] self.sync += [ If( self.diskdata.re, disk_data_available.eq(1), ), # Set false again on read If( active & disk_read, disk_reading.eq(0), If( disk_data_available, disk_data_wanted.eq(0), ).Else(disk_data_wanted.eq(1), ), disk_data_available.eq(0), ), If( iosel, # Disk II Controller Card in slot 6 # C0E0 PHASEOFF Stepper motor phase 0 off. # C0E1 PHASEON Stepper motor phase 0 on. # C0E2 PHASE1OFF Stepper motor phase 1 off. # C0E3 PHASElON Stepper motor phase 1 on. # C0E4 PHASE2OFF Stepper motor phase 2 off. # C0E5 PHASE2ON Stepper notor phase 2 on. # C0E6 PHASE3OFF Stepper motor phase 3 off. # C0E7 PHASE3ON Stepper motor phase 3 on. # C0E8 MOTOROFF Turn motor off. # C0E9 MOTORON Turn motor on. # C0EA DRV0EN Engage drive 1. # C0EB DRV1EN Engage drive 2. # C0EC Q6L Strobe Data Latch for I/O. # C0ED Q6H Load Data Latch. # C0EE Q7H Prepare latch for input (read from disk). # C0EF Q7L Prepare latch for output (write to disk). # Q7L with Q6L = Read # Q7L with Q6H = Sense Write Protect # Q7H with Q6L = Write # Q7H with Q6H = Load Write Latch If( addr[4:8] == 0xE, # (8|6) # Addresses 0-7 simply update a bit in the status register If(addr[0:4] == 0x0, disk_phase[0].eq(0)), If(addr[0:4] == 0x1, disk_phase[0].eq(1)), If(addr[0:4] == 0x2, disk_phase[1].eq(0)), If(addr[0:4] == 0x3, disk_phase[1].eq(1)), If(addr[0:4] == 0x4, disk_phase[2].eq(0)), If(addr[0:4] == 0x5, disk_phase[2].eq(1)), If(addr[0:4] == 0x6, disk_phase[3].eq(0)), If(addr[0:4] == 0x7, disk_phase[3].eq(1)), # Likewise, motor active and drive select update status If(addr[0:4] == 0x8, disk_motor.eq(0)), If(addr[0:4] == 0x9, disk_motor.eq(1)), If(addr[0:4] == 0xA, disk_drive.eq(0)), If(addr[0:4] == 0xB, disk_drive.eq(1)), # Write is ignored and read must be delayed one clock tick If(addr[0:4] == 0xC, disk_reading.eq(1)), #If(addr[0:4]==0xD, disk_ior_wp.eq(1)), #If(addr[0:4]==0xE, disk_write.eq(0)), #If(addr[0:4]==0xF, disk_write.eq(1)), ), ), ] #=============================================================================== # Video Output - Text Mode #=============================================================================== # The Apple II screen memory contains 8 segments containing 3 rows each in a 128 # byte block leaving 8 unused bytes in each of the 8 blocks To assist with # scroll detection, we convert memory addresses to screen coordinates. #=============================================================================== # Video memory - Frame Buffer access shortcuts fbsel = Signal() fb_r = Signal() fb_w = Signal() # Conversion from memory address to X,Y screen coordinates segment = Signal(3) triple = Signal(7) third = Signal(2) horiz = Signal(6) vert = Signal(5) move = Signal() scroll_active = Signal() scroll_match = Signal() scroll_start = Signal() scroll_end = Signal() scroll_read = Signal() scroll_write_valid = Signal() scroll_next_col = Signal() scroll_next_row = Signal() scroll_sequential = Signal() read_horiz = Signal(6) read_vert = Signal(5) repeat_active = Signal() repeat_match = Signal() repeat_start = Signal() repeat_end = Signal() repeat_next_col = Signal() repeat_next_row = Signal() repeat_sequential = Signal() # Registers shared by scroll and repeat compression circuits #horiz_start = Signal(max=40) #vert_start = Signal(max=24) #horiz_end = Signal(max=40) #vert_end = Signal(max=24) prev_horiz = Signal(max=40) prev_vert = Signal(max=24) prev_char = Signal(8) prev_start = Signal() push_save = Signal() push_saving = Signal() fifo_out = Signal(32) self.comb += [ # Detect access to frame memory: Address range 0x0400-0x7ff fbsel.eq((addr[10:15] == 0x1) & active), fb_r.eq(fbsel & ~wren), fb_w.eq(fbsel & wren), # Convert memory address to X,Y coordinates segment.eq(addr[7:10]), triple.eq(addr[0:7]), # TODO This generates reg - change to cause only wire If( triple >= 80, third.eq(2), horiz.eq(addr[0:7] - 80), ).Else( If( triple >= 40, third.eq(1), horiz.eq(addr[0:7] - 40), ).Else( third.eq(0), horiz.eq(addr[0:7]), )), vert.eq(Cat(segment, third)), # TODO Detect scroll - frame buffer read immediately followed by # frame buffer write to character on previous line, directly above. # Scroll is Right to Left (asm: DEY at FC90 in Autostart ROM) scroll_match.eq((horiz == read_horiz) & (vert + 1 == read_vert)), # & (din==read_char)) <== TODO Need to delay din by 1 cycle scroll_write_valid.eq(scroll_read & fb_w & scroll_match), scroll_start.eq(scroll_write_valid & ~scroll_active), # Scroll ends on any write that does not follow the required pattern scroll_end.eq(scroll_active & fb_w & ~scroll_write_valid), scroll_next_col.eq((horiz + 1 == prev_horiz) & (vert == prev_vert)), scroll_next_row.eq((horiz == 39) & (prev_horiz == 0) & (vert == prev_vert + 1)), scroll_sequential.eq(scroll_next_col | scroll_next_row), # Detect repeated charaters (spaces) # Clear is Left to Right (asm: INY at FCA2 in Autostart ROM) # repeat_match.eq(fb_w & (dout==prev_char)), # repeat_start.eq(repeat_match & repeat_sequential & ~repeat_active), # repeat_end.eq(fb_w & repeat_active & # (~repeat_match |~repeat_sequential)), # repeat_next_col.eq((horiz==prev_horiz+1) & (vert==prev_vert)), # repeat_next_row.eq((horiz==0) & (prev_horiz==39) & # (vert==prev_vert+1)), # repeat_sequential.eq(repeat_next_col | repeat_next_row), # repeat_sequential.eq(repeat_next_col), # This or the previous one # Place writes in the fifo self.display_fifo.din[8].eq(0), # Valid is calculated self.display_fifo.din[9].eq(0), # More is calculated #self.display_fifo.din[ 10].eq(move), self.display_fifo.din[11].eq(repeat_end), If( push_save, self.display_fifo.din[0:8].eq(prev_char), self.display_fifo.din[12].eq(prev_start), self.display_fifo.din[13].eq(scroll_end), #self.display_fifo.din[14:16].eq(0), # Reserved self.display_fifo.din[16:22].eq(prev_horiz ), # 2 bits padding self.display_fifo.din[24:29].eq( prev_vert), # 3 bits padding ).Elif( push_saving, # push_save will be valid on the next cycle - so push previous self.display_fifo.din[0:8].eq(prev_char), #self.display_fifo.din[ 8].eq(0), # Valid is calculated #self.display_fifo.din[ 9].eq(0), # More is calculated #self.display_fifo.din[ 10].eq(move), #self.display_fifo.din[ 11].eq(repeat_end), self.display_fifo.din[12].eq(scroll_start), self.display_fifo.din[13].eq(scroll_end), #self.display_fifo.din[14:16].eq(0), # Reserved self.display_fifo.din[16:22].eq(prev_horiz ), # 2 bits padding self.display_fifo.din[24:29].eq( prev_vert), # 3 bits padding ).Else( #self.display_fifo.din.eq(Cat(dout, horiz, vert, # move, scroll_start, scroll_end, repeat_start, repeat_end)), self.display_fifo.din[0:8].eq(dout), #self.display_fifo.din[ 8].eq(0), # Valid is calculated #self.display_fifo.din[ 9].eq(0), # More is calculated #self.display_fifo.din[ 10].eq(move), #self.display_fifo.din[ 11].eq(repeat_end), self.display_fifo.din[12].eq(scroll_start), self.display_fifo.din[13].eq(scroll_end), #self.display_fifo.din[14:16].eq(0), # Reserved self.display_fifo.din[16:22].eq(horiz), # 2 bits padding self.display_fifo.din[24:29].eq(vert), # 3 bits padding ), self.display_fifo.we.eq(push_save | repeat_end | scroll_start | scroll_end | (fb_w & ~scroll_active & ~repeat_active & ~repeat_start)), push_saving.eq(((repeat_end & ~repeat_sequential) | scroll_end) & ~push_save), # Retrieve characters from fifo self.display_fifo.re.eq(self.screen.we), self.screen.we.eq(self.display_fifo.re), self.screen.fields.Valid.eq(self.display_fifo.readable), self.screen.fields.More.eq(self.display_fifo.readable), self.screen.fields.Character.eq(fifo_out[0:8]), self.screen.fields.Horizontal.eq(fifo_out[16:22]), self.screen.fields.Vertical.eq(fifo_out[24:29]), #self.screen.fields.Move.eq(fifo_out[10]), self.screen.fields.Repeat.eq(fifo_out[11]), self.screen.fields.ScrollStart.eq(fifo_out[12]), self.screen.fields.ScrollEnd.eq(fifo_out[13]), ] self.sync += [ fifo_out.eq(self.display_fifo.dout), # Scroll If( scroll_start, scroll_active.eq(1), #horiz_start.eq(horiz), #vert_start.eq(vert), #horiz_end.eq(horiz), #vert_end.eq(vert), ), If( scroll_end, push_save.eq(1), scroll_active.eq(0), # These happen on any write to the frame buffer #prev_horiz.eq(horiz), #prev_vert.eq(vert), #prev_char.eq(dout), prev_start.eq(scroll_start), ), If( fb_r, If((scroll_read & (scroll_sequential | ~scroll_active)) | (repeat_active & repeat_sequential), # A faithful 6502 model issues a read of the target # address before the write cycle of an indirect store # instruction. Do nothing if we suspect the current read is # actually part of a store instruction. ). Else( scroll_read.eq(1), read_horiz.eq(horiz), read_vert.eq(vert), # TODO read_char should be din on the next clock cycle # read_char.eq(dout), ), ), # Write to the frame buffer: remember the location and character # that generate the sequential and repeat signals which are used # by the scroll and clear functions. Also, update scroll signals. If( fb_w, scroll_read.eq(0), prev_vert.eq(vert), prev_horiz.eq(horiz), prev_char.eq(dout), # Repeat - Mostly needed for screen clearing operations but made # generic to conserve bandwidth and allow any character to be # repeated. If( repeat_match & repeat_sequential, # Supress output, begin sequence if not started already # Store location and character as this will be needed if the # following write is not sequential # Setting repeat is redundant if already active repeat_active.eq(1), ).Elif( repeat_active & ~repeat_sequential, # The cursor moved and we must terminate repeat mode # indicating the last character in the sequence. This is # required whether or not the same character is present. # Output saved location with Repeat flag set to mark end. # Then output current signal set on next cycle push_save.eq(1), prev_start.eq(scroll_start), repeat_active.eq(0), ).Else( # Push current character on stack either because: # a. character is different breaking repeat # b. character is different preventing repeat # c. location is not sequential breaking repeat # d. location is not sequential preventing repeat # Cases a,c need to clear repeat. This is irrelevant for b,d repeat_active.eq(0), ), ), # We can safely use the cycle after a write to frame memory as a # second push into the character fifo knowing that the only time the # 6502 has two consecutive write cycles is the JSR instruction which # writes the return address onto the stack, and the RMW instructions # INC, DEC, LSR, ASR, ROL, ROR which write the original and the new # values in two consecutive cycles. It is extremely poor programming # practice to keep the stack inside the frame buffer while clearing # or scrolling the screen so no special handling of these cases is # taken. If( push_save, # Auto-clear after one clock cycle push_save.eq(0), ), ]
def __init__(self, pads, default=_default_edid): self._hpd_notif = CSRStatus() self._hpd_en = CSRStorage() mem_size = len(default) assert mem_size % 128 == 0 self.specials.mem = Memory(8, mem_size, init=default) # # # # HPD if hasattr(pads, "hpd_notif"): if hasattr(getattr(pads, "hpd_notif"), "inverted"): hpd_notif_n = Signal() self.comb += hpd_notif_n.eq(~pads.hpd_notif) self.specials += MultiReg(hpd_notif_n, self._hpd_notif.status) else: self.specials += MultiReg(pads.hpd_notif, self._hpd_notif.status) else: self.comb += self._hpd_notif.status.eq(1) if hasattr(pads, "hpd_en"): self.comb += pads.hpd_en.eq(self._hpd_en.storage) # EDID scl_raw = Signal() sda_i = Signal() sda_raw = Signal() sda_drv = Signal() _sda_drv_reg = Signal() _sda_i_async = Signal() self.sync += _sda_drv_reg.eq(sda_drv) pad_scl = getattr(pads, "scl") if hasattr(pad_scl, "inverted"): self.specials += MultiReg(~pads.scl, scl_raw) else: self.specials += MultiReg(pads.scl, scl_raw) if hasattr(pads, "sda_drv"): pads_sda = getattr(pads, "sda") if hasattr(pads_sda, "inverted"): self.specials += MultiReg(~pads.sda, sda_raw) else: self.specials += MultiReg(pads.sda, sda_raw) self.comb += pads.sda_drv.eq(_sda_drv_reg) else: self.specials += [ Tristate(pads.sda, 0, _sda_drv_reg, _sda_i_async), MultiReg(_sda_i_async, sda_raw) ] # for debug self.scl = scl_raw self.sda_i = sda_i self.sda_o = Signal() self.comb += self.sda_o.eq(~_sda_drv_reg) self.sda_oe = _sda_drv_reg scl_i = Signal() samp_count = Signal(6) samp_carry = Signal() self.sync += [ Cat(samp_count, samp_carry).eq(samp_count + 1), If(samp_carry, scl_i.eq(scl_raw), sda_i.eq(sda_raw)) ] scl_r = Signal() sda_r = Signal() scl_rising = Signal() sda_rising = Signal() sda_falling = Signal() self.sync += [scl_r.eq(scl_i), sda_r.eq(sda_i)] self.comb += [ scl_rising.eq(scl_i & ~scl_r), sda_rising.eq(sda_i & ~sda_r), sda_falling.eq(~sda_i & sda_r) ] start = Signal() self.comb += start.eq(scl_i & sda_falling) din = Signal(8) counter = Signal(max=9) self.sync += [ If(start, counter.eq(0)), If( scl_rising, If(counter == 8, counter.eq(0)).Else(counter.eq(counter + 1), din.eq(Cat(sda_i, din[:7])))) ] self.din = din self.counter = counter is_read = Signal() update_is_read = Signal() self.sync += If(update_is_read, is_read.eq(din[0])) offset_counter = Signal(max=mem_size) oc_load = Signal() oc_inc = Signal() self.sync += \ If(oc_load, offset_counter.eq(din) ).Elif(oc_inc, offset_counter.eq(offset_counter + 1) ) rdport = self.mem.get_port() self.specials += rdport self.comb += rdport.adr.eq(offset_counter) data_bit = Signal() zero_drv = Signal() data_drv = Signal() self.comb += \ If(zero_drv, sda_drv.eq(1) ).Elif(data_drv, sda_drv.eq(~data_bit) ) data_drv_en = Signal() data_drv_stop = Signal() self.sync += \ If(data_drv_en, data_drv.eq(1) ).Elif(data_drv_stop, data_drv.eq(0) ) self.sync += \ If(data_drv_en, chooser(rdport.dat_r, counter, data_bit, 8, reverse=True) ) self.submodules.fsm = fsm = FSM() fsm.act("WAIT_START") fsm.act( "RCV_ADDRESS", If( counter == 8, If(din[1:] == 0x50, update_is_read.eq(1), NextState("ACK_ADDRESS0")).Else(NextState("WAIT_START")))) fsm.act("ACK_ADDRESS0", If(~scl_i, NextState("ACK_ADDRESS1"))) fsm.act("ACK_ADDRESS1", zero_drv.eq(1), If(scl_i, NextState("ACK_ADDRESS2"))) fsm.act( "ACK_ADDRESS2", zero_drv.eq(1), If(~scl_i, If(is_read, NextState("READ")).Else(NextState("RCV_OFFSET")))) fsm.act("RCV_OFFSET", If(counter == 8, oc_load.eq(1), NextState("ACK_OFFSET0"))) fsm.act("ACK_OFFSET0", If(~scl_i, NextState("ACK_OFFSET1"))) fsm.act("ACK_OFFSET1", zero_drv.eq(1), If(scl_i, NextState("ACK_OFFSET2"))) fsm.act("ACK_OFFSET2", zero_drv.eq(1), If(~scl_i, NextState("RCV_ADDRESS"))) fsm.act( "READ", If( ~scl_i, If(counter == 8, data_drv_stop.eq(1), NextState("ACK_READ")).Else(data_drv_en.eq(1)))) fsm.act( "ACK_READ", If(scl_rising, oc_inc.eq(1), If(sda_i, NextState("WAIT_START")).Else(NextState("READ")))) for state in fsm.actions.keys(): fsm.act(state, If(start, NextState("RCV_ADDRESS"))) if hasattr(pads, "hpd_en"): fsm.act(state, If(~self._hpd_en.storage, NextState("WAIT_START")))