From: Florent Kermarrec Date: Thu, 26 Mar 2015 21:24:47 +0000 (+0100) Subject: sdram/phy: add simphy (software memtest OK in simulation with MT48LC4M16) X-Git-Tag: 24jan2021_ls180~2434 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=e6de4b1bf988fadd4acf2b773baaf22ecc3835df;p=litex.git sdram/phy: add simphy (software memtest OK in simulation with MT48LC4M16) --- diff --git a/misoclib/mem/sdram/phy/simphy.py b/misoclib/mem/sdram/phy/simphy.py new file mode 100644 index 00000000..4ff910d8 --- /dev/null +++ b/misoclib/mem/sdram/phy/simphy.py @@ -0,0 +1,199 @@ +# This file is Copyright (c) 2015 Florent Kermarrec +# License: BSD + +# SDRAM simulation PHY at DFI level +# Status: +# - tested against software memtest with SDR with Verilator. +# TODO: +# - expose phy_settings to user +# - test with DDR, LPDDR, DDR2 and DDR3 +# - add $display support to Migen and manage timing violations? + +from migen.fhdl.std import * +from migen.fhdl.specials import * +from misoclib.mem.sdram.phy.dfi import * +from misoclib.mem import sdram + +class Bank(Module): + def __init__(self, data_width, nrows, ncols): + self.activate = Signal() + self.activate_row = Signal(max=nrows) + self.precharge = Signal() + + self.write = Signal() + self.write_col = Signal(max=ncols) + self.write_data = Signal(data_width) + self.write_mask = Signal(data_width//8) + + self.read = Signal() + self.read_col = Signal(max=ncols) + self.read_data = Signal(data_width) + + ### + active = Signal() + row = Signal(max=nrows) + + self.sync += \ + If(self.precharge, + active.eq(0), + ).Elif(self.activate, + active.eq(1), + row.eq(self.activate_row) + ) + + self.specials.mem = mem = Memory(data_width, nrows*ncols) + self.specials.write_port = write_port = mem.get_port(write_capable=True, we_granularity=8) + self.specials.read_port = read_port = mem.get_port(async_read=True) + self.comb += [ + If(active, + write_port.adr.eq(row*ncols | self.write_col), + write_port.dat_w.eq(self.write_data), + write_port.we.eq(Replicate(self.write, data_width//8) & ~self.write_mask), + If(self.read, + read_port.adr.eq(row*ncols | self.read_col), + self.read_data.eq(read_port.dat_r) + ) + ) + ] + +class DFIPhase(Module): + def __init__(self, dfi, n): + phase = getattr(dfi, "p"+str(n)) + + self.bank = phase.bank + self.address = phase.address + + self.wrdata = phase.wrdata + self.wrdata_mask = phase.wrdata_mask + + self.rddata = phase.rddata + self.rddata_valid = phase.rddata_valid + + self.activate = Signal() + self.precharge = Signal() + self.write = Signal() + self.read = Signal() + + ### + self.comb += [ + If(~phase.cs_n & ~phase.ras_n & phase.cas_n, + self.activate.eq(phase.we_n), + self.precharge.eq(~phase.we_n) + ), + If(~phase.cs_n & phase.ras_n & ~phase.cas_n, + self.write.eq(~phase.we_n), + self.read.eq(phase.we_n) + ) + ] + +class SDRAMPHYSim(Module): + def __init__(self, module, nmodules=1): + addressbits = module.geom_settings.addressbits + databits = module.geom_settings.databits + bankbits = module.geom_settings.bankbits + rowbits = module.geom_settings.rowbits + colbits = module.geom_settings.colbits + + # XXX expose this to user + self.settings = sdram.PhySettings( + memtype=module.memtype, + dfi_databits=databits, + nphases=1, + rdphase=0, + wrphase=0, + rdcmdphase=0, + wrcmdphase=0, + cl=2, + read_latency=4, + write_latency=0 + ) + self.module = module + + self.dfi = Interface(addressbits, bankbits, databits) + + ### + nbanks = 2**bankbits + nrows = 2**rowbits + ncols = 2**colbits + data_width = databits*nmodules + + # DFI phases + phases = [DFIPhase(self.dfi, n) for n in range(self.settings.nphases)] + self.submodules += phases + + # banks + banks = [Bank(data_width, nrows, ncols) for i in range(nbanks)] + self.submodules += banks + + # connect DFI phases to banks (cmds, write datapath) + for nb, bank in enumerate(banks): + # bank activate + activates = Signal(len(phases)) + cases = {} + for np, phase in enumerate(phases): + self.comb += activates[np].eq(phase.activate) + cases[2**np] = [ + bank.activate.eq(phase.bank == nb), + bank.activate_row.eq(phase.address) + ] + self.comb += Case(activates, cases) + + # bank precharge + precharges = Signal(len(phases)) + cases = {} + for np, phase in enumerate(phases): + self.comb += precharges[np].eq(phase.precharge) + cases[2**np] = [ + bank.precharge.eq((phase.bank == nb) | phase.address[10]) + ] + self.comb += Case(precharges, cases) + + # bank writes + writes = Signal(len(phases)) + cases = {} + for np, phase in enumerate(phases): + self.comb += writes[np].eq(phase.write) + cases[2**np] = [ + bank.write.eq(phase.bank == nb), + bank.write_col.eq(phase.address) + ] + self.comb += Case(writes, cases) + self.comb += [ + bank.write_data.eq(Cat(*[phase.wrdata for phase in phases])), + bank.write_mask.eq(Cat(*[phase.wrdata_mask for phase in phases])) + ] + + # bank reads + reads = Signal(len(phases)) + read_data = Signal(data_width) + cases = {} + for np, phase in enumerate(phases): + self.comb += reads[np].eq(phase.read) + cases[2**np] = [ + bank.read.eq(phase.bank == nb), + bank.read_col.eq(phase.address) + ] + self.comb += Case(reads, cases) + + # connect banks to DFI phases (cmds, read datapath) + banks_read = Signal() + banks_read_data = Signal(data_width) + self.comb += [ + banks_read.eq(optree("|", [bank.read for bank in banks])), + banks_read_data.eq(optree("|", [bank.read_data for bank in banks])) + ] + # simulate read latency + for i in range(self.settings.read_latency): + new_banks_read = Signal() + new_banks_read_data = Signal(data_width) + self.sync += [ + new_banks_read.eq(banks_read), + new_banks_read_data.eq(banks_read_data) + ] + banks_read = new_banks_read + banks_read_data = new_banks_read_data + + self.comb += [ + Cat(iter([phase.rddata_valid for phase in phases])).eq(banks_read), + Cat(iter([phase.rddata for phase in phases])).eq(banks_read_data) + ]