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 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): 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 do_finalize(self, busword): nwords = (self.size + busword - 1)//busword # construct storage assert busword == 8 and nwords == 1, 'Only 1-byte CSRStorage is supposed' sc = CSR(self.size, self.name) self.simple_csrs.append(sc) # for bit in range(self.size): access = self.field_access.get(bit, CSRAccess.ReadWrite) if access == CSRAccess.WriteOnly: raise NotADirectoryError() # write only if the register is not read-only if access != CSRAccess.ReadOnly: if bit in self.clear_on_write: # write 0 only if there is write and it is writing 1 self.sync += If(sc.re & sc.r[bit], self.storage[bit].eq(0)) else: self.sync += If(sc.re, self.storage[bit].eq(sc.r[bit])) self.sync += self.re.eq(sc.re) # read self.comb += sc.w.eq(self.storage)
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, clocks, log_level, auto_precharge=False, with_refresh=True, trace_reset=0, disable_delay=False, masked_write=True, double_rate_phy=False, finish_after_memtest=False, **kwargs): platform = Platform() sys_clk_freq = clocks["sys"]["freq_hz"] # SoCCore ---------------------------------------------------------------------------------- super().__init__(platform, clk_freq=sys_clk_freq, ident="LiteX Simulation", ident_version=True, cpu_variant="minimal", **kwargs) # CRG -------------------------------------------------------------------------------------- self.submodules.crg = _CRG(platform, clocks.names()) # Debugging -------------------------------------------------------------------------------- platform.add_debug(self, reset=trace_reset) # LPDDR4 ----------------------------------------------------------------------------------- sdram_module = litedram_modules.MT53E256M16D1(sys_clk_freq, "1:8") pads = platform.request("lpddr4") sim_phy_cls = DoubleRateLPDDR4SimPHY if double_rate_phy else LPDDR4SimPHY self.submodules.ddrphy = sim_phy_cls( sys_clk_freq=sys_clk_freq, aligned_reset_zero=True, masked_write=masked_write, ) # fake delays (make no nsense in simulation, but sdram.c expects them) self.ddrphy._rdly_dq_rst = CSR() self.ddrphy._rdly_dq_inc = CSR() self.add_csr("ddrphy") for p in [ "clk_p", "clk_n", "cke", "odt", "reset_n", "cs", "ca", "dq", "dqs", "dmi" ]: self.comb += getattr(pads, p).eq(getattr(self.ddrphy.pads, p)) controller_settings = ControllerSettings() controller_settings.auto_precharge = auto_precharge controller_settings.with_refresh = with_refresh self.add_sdram("sdram", phy=self.ddrphy, module=sdram_module, origin=self.mem_map["main_ram"], size=kwargs.get("max_sdram_size", 0x40000000), l2_cache_size=kwargs.get("l2_size", 8192), l2_cache_min_data_width=kwargs.get( "min_l2_data_width", 128), l2_cache_reverse=False, controller_settings=controller_settings) # Reduce memtest size for simulation speedup self.add_constant("MEMTEST_DATA_SIZE", 8 * 1024) self.add_constant("MEMTEST_ADDR_SIZE", 8 * 1024) # LPDDR4 Sim ------------------------------------------------------------------------------- self.submodules.lpddr4sim = LPDDR4Sim( pads=self.ddrphy.pads, cl=self.sdram.controller.settings.phy.cl, cwl=self.sdram.controller.settings.phy.cwl, sys_clk_freq=sys_clk_freq, log_level=log_level, disable_delay=disable_delay, ) self.add_csr("lpddr4sim") self.add_constant("CONFIG_SIM_DISABLE_BIOS_PROMPT") if disable_delay: self.add_constant("CONFIG_DISABLE_DELAYS") if finish_after_memtest: self.submodules.ddrctrl = LiteDRAMCoreControl() self.add_csr("ddrctrl") self.sync += If(self.ddrctrl.init_done.storage, Finish()) # Reuse DFITimingsChecker from phy/model.py nphases = self.sdram.controller.settings.phy.nphases timings = {"tCK": (1e9 / sys_clk_freq) / nphases} for name in _speedgrade_timings + _technology_timings: timings[name] = sdram_module.get(name) self.submodules.dfi_timings_checker = DFITimingsChecker( dfi=self.ddrphy.dfi, nbanks=2**self.sdram.controller.settings.geom.bankbits, nphases=nphases, timings=timings, refresh_mode=sdram_module.timing_settings.fine_refresh_mode, memtype=self.sdram.controller.settings.phy.memtype, verbose=False, ) # Debug info ------------------------------------------------------------------------------- def dump(obj): print() print(" " + obj.__class__.__name__) print(" " + "-" * len(obj.__class__.__name__)) d = obj if isinstance(obj, dict) else vars(obj) for var, val in d.items(): if var == "self": continue if isinstance(val, Signal): val = "Signal(reset={})".format(val.reset.value) print(" {}: {}".format(var, val)) print("=" * 80) dump(clocks) dump(self.ddrphy.settings) dump(sdram_module.geom_settings) dump(sdram_module.timing_settings) print() print("=" * 80)
def __init__(self): # set from software self.finish = CSR() self.sync += If(self.finish.re, Finish())
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, aligned_reset_zero=False, **kwargs): pads = LPDDR4SimulationPads() self.submodules += pads super().__init__(pads, ser_latency=Latency(sys=Serializer.LATENCY), des_latency=Latency(sys=Deserializer.LATENCY), phytype="LPDDR4SimPHY", **kwargs) # fake delays (make no nsense in simulation, but sdram.c expects them) self.settings.read_leveling = True self.settings.delays = 1 self._rdly_dq_rst = CSR() self._rdly_dq_inc = CSR() delay = lambda sig, cycles: delayed(self, sig, cycles=cycles) sdr = dict(clkdiv="sys", clk="sys8x") sdr_90 = dict(clkdiv="sys", clk="sys8x_90") ddr = dict(clkdiv="sys", clk="sys8x_ddr") ddr_90 = dict(clkdiv="sys", clk="sys8x_90_ddr") if aligned_reset_zero: sdr["reset_cnt"] = 0 ddr["reset_cnt"] = 0 # Clock is shifted 180 degrees to get rising edge in the middle of SDR signals. # To achieve that we send negated clock on clk (clk_p). self.ser(i=~self.out.clk, o=self.pads.clk, name='clk', **ddr) self.ser(i=self.out.cke, o=self.pads.cke, name='cke', **sdr) self.ser(i=self.out.odt, o=self.pads.odt, name='odt', **sdr) self.ser(i=self.out.reset_n, o=self.pads.reset_n, name='reset_n', **sdr) # Command/address self.ser(i=self.out.cs, o=self.pads.cs, name='cs', **sdr) for i in range(6): self.ser(i=self.out.ca[i], o=self.pads.ca[i], name=f'ca{i}', **sdr) # Tristate I/O (separate for simulation) for i in range(self.databits // 8): self.ser(i=self.out.dmi_o[i], o=self.pads.dmi_o[i], name=f'dmi_o{i}', **ddr) self.des(o=self.out.dmi_i[i], i=self.pads.dmi[i], name=f'dmi_i{i}', **ddr) self.ser(i=self.out.dqs_o[i], o=self.pads.dqs_o[i], name=f'dqs_o{i}', **ddr_90) self.des(o=self.out.dqs_i[i], i=self.pads.dqs[i], name=f'dqs_i{i}', **ddr_90) for i in range(self.databits): self.ser(i=self.out.dq_o[i], o=self.pads.dq_o[i], name=f'dq_o{i}', **ddr) self.des(o=self.out.dq_i[i], i=self.pads.dq[i], name=f'dq_i{i}', **ddr) # Output enable signals self.comb += [ self.pads.dmi_oe.eq( delay(self.out.dmi_oe, cycles=Serializer.LATENCY)), self.pads.dqs_oe.eq( delay(self.out.dqs_oe, cycles=Serializer.LATENCY)), self.pads.dq_oe.eq(delay(self.out.dq_oe, cycles=Serializer.LATENCY)), ]
def __init__(self, mems=None, data_ins=None, N_CHANNELS=1, N_BITS=16): """ mems list of memory objects of length N_CHANNELS acquisition starts after * rising edge on self.trigger * data_in of the selected channel crossing trig_level """ # uint16, on `sample` clock domain if data_ins: self.data_ins = data_ins N_CHANNELS = len(data_ins) else: self.data_ins = [Signal((N_BITS, True)) for i in range(N_CHANNELS)] self.trigger = Signal() self.busy = Signal() ### if mems is None: mems = [Memory(N_BITS, 12) for i in range(N_CHANNELS)] trig = Signal() # writing trig_csr triggers a single shot acquisition (value dont matter) # reading trig_csr reads the 1 when an acquisiton is in progress self.trig_csr = CSR() self.specials += MultiReg(self.busy, self.trig_csr.w) self.submodules.trig_sync = PulseSynchronizer("sys", "sample") self.comb += [ self.trig_sync.i.eq(self.trig_csr.re), trig.eq(self.trigger | self.trig_sync.o) ] # select the trigger level (signed int) self.trig_level = CSRStorage(16) self.trig_level.storage.signed = True trig_level_ = Signal((16, True)) self.specials += MultiReg(self.trig_level.storage, trig_level_, 'sample') # force trigger self.trig_force = CSRStorage(1) trig_force_ = Signal() self.specials += MultiReg(self.trig_force.storage, trig_force_, 'sample') # select the channel to trigger on self.trig_channel = CSRStorage(8) trig_channel_ = Signal() self.specials += MultiReg(self.trig_channel.storage, trig_channel_, 'sample') # data stream of the channel to trigger on data_trigger = Signal((N_BITS, True)) data_trigger_d = Signal((N_BITS, True)) is_trigger = Signal() self.comb += [ # select one of the channels to trigger on, Case(trig_channel_, {k: data_trigger.eq(v) for k, v in enumerate(self.data_ins)}), # Pulse `is_trigger` high when sample passes the trigger threshold is_trigger.eq((data_trigger_d < trig_level_) & (data_trigger >= trig_level_) | trig_force_) ] self.sync.sample += data_trigger_d.eq(data_trigger) mem_we = Signal() mem_addr = Signal(max=mems[0].depth) self.submodules.fsm = ClockDomainsRenamer("sample")(FSM()) self.fsm.act("WAIT_TRIGGER", If(trig, NextState("WAIT_LEVEL"))) self.fsm.act( "WAIT_LEVEL", If(is_trigger, mem_we.eq(1), NextValue(mem_addr, mem_addr + 1), NextState("ACQUIRE"))) self.fsm.act( "ACQUIRE", mem_we.eq(1), NextValue(mem_addr, mem_addr + 1), If(mem_addr >= mems[0].depth - 1, NextState("WAIT_TRIGGER"), NextValue(mem_addr, 0))) self.sync.sample += self.busy.eq(~self.fsm.ongoing('WAIT_TRIGGER')) for mem, data_in in zip(mems, self.data_ins): self.specials += mem p1 = mem.get_port(write_capable=True, clock_domain="sample") self.specials += p1 self.comb += [ p1.dat_w.eq(data_in), p1.adr.eq(mem_addr), p1.we.eq(mem_we) ]