2015-09-25 06:43:20 -04:00
|
|
|
from functools import reduce
|
|
|
|
from operator import or_
|
|
|
|
|
2015-09-22 12:36:47 -04:00
|
|
|
from migen import *
|
2014-10-31 18:36:06 -04:00
|
|
|
from migen.genlib.fsm import FSM, NextState
|
2015-09-25 06:43:20 -04:00
|
|
|
from migen.genlib.misc import WaitTimer
|
2014-10-31 18:36:06 -04:00
|
|
|
|
2015-09-25 06:43:20 -04:00
|
|
|
from misoc.interconnect import dfi as dfibus
|
|
|
|
from misoc.interconnect import wishbone
|
2015-02-27 10:55:27 -05:00
|
|
|
|
2015-04-13 10:47:22 -04:00
|
|
|
|
2014-10-31 18:36:06 -04:00
|
|
|
class _AddressSlicer:
|
2015-04-13 10:19:55 -04:00
|
|
|
def __init__(self, colbits, bankbits, rowbits, address_align):
|
|
|
|
self.colbits = colbits
|
|
|
|
self.bankbits = bankbits
|
|
|
|
self.rowbits = rowbits
|
|
|
|
self.address_align = address_align
|
2015-05-29 06:26:34 -04:00
|
|
|
self.addressbits = colbits - address_align + bankbits + rowbits
|
2015-04-13 10:19:55 -04:00
|
|
|
|
|
|
|
def row(self, address):
|
2015-05-29 06:26:34 -04:00
|
|
|
split = self.bankbits + self.colbits - self.address_align
|
2015-04-13 10:19:55 -04:00
|
|
|
if isinstance(address, int):
|
|
|
|
return address >> split
|
|
|
|
else:
|
2015-05-29 06:26:34 -04:00
|
|
|
return address[split:self.addressbits]
|
2015-04-13 10:19:55 -04:00
|
|
|
|
|
|
|
def bank(self, address):
|
2015-05-29 06:26:34 -04:00
|
|
|
split = self.colbits - self.address_align
|
2015-04-13 10:19:55 -04:00
|
|
|
if isinstance(address, int):
|
2015-05-29 06:26:34 -04:00
|
|
|
return (address & (2**(split + self.bankbits) - 1)) >> split
|
2015-04-13 10:19:55 -04:00
|
|
|
else:
|
2015-05-29 06:26:34 -04:00
|
|
|
return address[split:split+self.bankbits]
|
2015-04-13 10:19:55 -04:00
|
|
|
|
|
|
|
def col(self, address):
|
2015-05-29 06:26:34 -04:00
|
|
|
split = self.colbits - self.address_align
|
2015-04-13 10:19:55 -04:00
|
|
|
if isinstance(address, int):
|
|
|
|
return (address & (2**split - 1)) << self.address_align
|
|
|
|
else:
|
|
|
|
return Cat(Replicate(0, self.address_align), address[:split])
|
2014-10-31 18:36:06 -04:00
|
|
|
|
2015-04-13 10:47:22 -04:00
|
|
|
|
2015-09-25 06:43:20 -04:00
|
|
|
@ResetInserter()
|
|
|
|
@CEInserter()
|
2015-05-29 06:26:34 -04:00
|
|
|
class _Bank(Module):
|
|
|
|
def __init__(self, geom_settings):
|
|
|
|
self.open = Signal()
|
|
|
|
self.row = Signal(geom_settings.rowbits)
|
|
|
|
|
|
|
|
self.idle = Signal(reset=1)
|
|
|
|
self.hit = Signal()
|
|
|
|
|
|
|
|
# # #
|
|
|
|
|
|
|
|
row = Signal(geom_settings.rowbits)
|
|
|
|
self.sync += \
|
|
|
|
If(self.open,
|
|
|
|
self.idle.eq(0),
|
|
|
|
row.eq(self.row)
|
|
|
|
)
|
|
|
|
self.comb += self.hit.eq(~self.idle & (self.row == row))
|
|
|
|
|
|
|
|
|
2014-10-31 18:36:06 -04:00
|
|
|
class Minicon(Module):
|
2015-04-13 10:19:55 -04:00
|
|
|
def __init__(self, phy_settings, geom_settings, timing_settings):
|
|
|
|
if phy_settings.memtype in ["SDR"]:
|
2015-04-13 11:16:12 -04:00
|
|
|
burst_length = phy_settings.nphases*1 # command multiplication*SDR
|
2015-04-13 10:19:55 -04:00
|
|
|
elif phy_settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]:
|
2015-04-13 11:16:12 -04:00
|
|
|
burst_length = phy_settings.nphases*2 # command multiplication*DDR
|
2015-06-02 13:35:00 -04:00
|
|
|
burst_width = phy_settings.dfi_databits*phy_settings.nphases
|
2015-04-13 10:19:55 -04:00
|
|
|
address_align = log2_int(burst_length)
|
|
|
|
|
2015-05-29 06:26:34 -04:00
|
|
|
# # #
|
2015-04-13 10:19:55 -04:00
|
|
|
|
|
|
|
self.dfi = dfi = dfibus.Interface(geom_settings.addressbits,
|
|
|
|
geom_settings.bankbits,
|
|
|
|
phy_settings.dfi_databits,
|
|
|
|
phy_settings.nphases)
|
|
|
|
|
2015-06-02 13:35:00 -04:00
|
|
|
self.bus = bus = wishbone.Interface(burst_width)
|
2015-04-13 10:19:55 -04:00
|
|
|
|
2015-05-29 06:26:34 -04:00
|
|
|
rdphase = phy_settings.rdphase
|
|
|
|
wrphase = phy_settings.wrphase
|
2015-04-13 10:19:55 -04:00
|
|
|
|
2015-05-29 06:26:34 -04:00
|
|
|
precharge_all = Signal()
|
|
|
|
activate = Signal()
|
|
|
|
refresh = Signal()
|
|
|
|
write = Signal()
|
|
|
|
read = Signal()
|
|
|
|
|
|
|
|
# Compute current column, bank and row from wishbone address
|
|
|
|
slicer = _AddressSlicer(geom_settings.colbits,
|
|
|
|
geom_settings.bankbits,
|
|
|
|
geom_settings.rowbits,
|
|
|
|
address_align)
|
|
|
|
|
|
|
|
# Manage banks
|
|
|
|
bank_idle = Signal()
|
|
|
|
bank_hit = Signal()
|
|
|
|
|
|
|
|
banks = []
|
|
|
|
for i in range(2**geom_settings.bankbits):
|
|
|
|
bank = _Bank(geom_settings)
|
2015-04-13 10:19:55 -04:00
|
|
|
self.comb += [
|
2015-05-29 06:26:34 -04:00
|
|
|
bank.open.eq(activate),
|
|
|
|
bank.reset.eq(precharge_all),
|
|
|
|
bank.row.eq(slicer.row(bus.adr))
|
2015-04-13 10:19:55 -04:00
|
|
|
]
|
2015-05-29 06:26:34 -04:00
|
|
|
banks.append(bank)
|
|
|
|
self.submodules += banks
|
2015-04-13 10:19:55 -04:00
|
|
|
|
2015-05-29 06:26:34 -04:00
|
|
|
cases = {}
|
|
|
|
for i, bank in enumerate(banks):
|
|
|
|
cases[i] = [bank.ce.eq(1)]
|
|
|
|
self.comb += Case(slicer.bank(bus.adr), cases)
|
2015-04-13 10:19:55 -04:00
|
|
|
|
2015-05-29 06:26:34 -04:00
|
|
|
self.comb += [
|
2015-09-25 06:43:20 -04:00
|
|
|
bank_hit.eq(reduce(or_, [bank.hit & bank.ce for bank in banks])),
|
|
|
|
bank_idle.eq(reduce(or_, [bank.idle & bank.ce for bank in banks])),
|
2015-04-13 10:19:55 -04:00
|
|
|
]
|
|
|
|
|
2015-05-29 06:26:34 -04:00
|
|
|
# Timings
|
|
|
|
write2precharge_timer = WaitTimer(2 + timing_settings.tWR - 1)
|
|
|
|
self.submodules += write2precharge_timer
|
|
|
|
self.comb += write2precharge_timer.wait.eq(~write)
|
|
|
|
|
|
|
|
refresh_timer = WaitTimer(timing_settings.tREFI)
|
|
|
|
self.submodules += refresh_timer
|
|
|
|
self.comb += refresh_timer.wait.eq(~refresh)
|
|
|
|
|
|
|
|
# Main FSM
|
|
|
|
self.submodules.fsm = fsm = FSM()
|
2015-04-13 10:19:55 -04:00
|
|
|
fsm.act("IDLE",
|
2015-05-29 06:26:34 -04:00
|
|
|
If(refresh_timer.done,
|
|
|
|
NextState("PRECHARGE-ALL")
|
2015-04-13 10:19:55 -04:00
|
|
|
).Elif(bus.stb & bus.cyc,
|
2015-05-29 06:26:34 -04:00
|
|
|
If(bank_hit,
|
|
|
|
If(bus.we,
|
|
|
|
NextState("WRITE")
|
|
|
|
).Else(
|
|
|
|
NextState("READ")
|
|
|
|
)
|
|
|
|
).Elif(~bank_idle,
|
|
|
|
If(write2precharge_timer.done,
|
|
|
|
NextState("PRECHARGE")
|
|
|
|
)
|
|
|
|
).Else(
|
2015-04-13 10:19:55 -04:00
|
|
|
NextState("ACTIVATE")
|
2015-05-29 06:26:34 -04:00
|
|
|
)
|
2015-04-13 10:19:55 -04:00
|
|
|
)
|
|
|
|
)
|
|
|
|
fsm.act("READ",
|
2015-05-29 06:26:34 -04:00
|
|
|
read.eq(1),
|
2015-04-13 10:19:55 -04:00
|
|
|
dfi.phases[rdphase].ras_n.eq(1),
|
|
|
|
dfi.phases[rdphase].cas_n.eq(0),
|
|
|
|
dfi.phases[rdphase].we_n.eq(1),
|
|
|
|
dfi.phases[rdphase].rddata_en.eq(1),
|
2015-05-29 06:26:34 -04:00
|
|
|
NextState("WAIT-READ-DONE"),
|
2015-04-13 10:19:55 -04:00
|
|
|
)
|
2015-05-29 06:26:34 -04:00
|
|
|
fsm.act("WAIT-READ-DONE",
|
2015-04-13 10:19:55 -04:00
|
|
|
If(dfi.phases[rdphase].rddata_valid,
|
2015-05-29 06:26:34 -04:00
|
|
|
bus.ack.eq(1),
|
|
|
|
NextState("IDLE")
|
2015-04-13 10:19:55 -04:00
|
|
|
)
|
|
|
|
)
|
|
|
|
fsm.act("WRITE",
|
2015-05-29 06:26:34 -04:00
|
|
|
write.eq(1),
|
2015-04-13 10:19:55 -04:00
|
|
|
dfi.phases[wrphase].ras_n.eq(1),
|
|
|
|
dfi.phases[wrphase].cas_n.eq(0),
|
|
|
|
dfi.phases[wrphase].we_n.eq(0),
|
|
|
|
dfi.phases[wrphase].wrdata_en.eq(1),
|
2015-05-29 06:26:34 -04:00
|
|
|
NextState("WRITE-LATENCY")
|
|
|
|
)
|
|
|
|
fsm.act("WRITE-ACK",
|
2015-04-13 10:19:55 -04:00
|
|
|
bus.ack.eq(1),
|
|
|
|
NextState("IDLE")
|
|
|
|
)
|
2015-05-29 06:26:34 -04:00
|
|
|
fsm.act("PRECHARGE-ALL",
|
|
|
|
precharge_all.eq(1),
|
2015-04-13 10:19:55 -04:00
|
|
|
dfi.phases[rdphase].ras_n.eq(0),
|
|
|
|
dfi.phases[rdphase].cas_n.eq(1),
|
|
|
|
dfi.phases[rdphase].we_n.eq(0),
|
|
|
|
NextState("PRE-REFRESH")
|
|
|
|
)
|
|
|
|
fsm.act("PRECHARGE",
|
2015-05-29 06:26:34 -04:00
|
|
|
# do no reset bank since we are going to re-open it
|
|
|
|
dfi.phases[0].ras_n.eq(0),
|
|
|
|
dfi.phases[0].cas_n.eq(1),
|
|
|
|
dfi.phases[0].we_n.eq(0),
|
|
|
|
NextState("TRP")
|
2015-04-13 10:19:55 -04:00
|
|
|
)
|
|
|
|
fsm.act("ACTIVATE",
|
2015-05-29 06:26:34 -04:00
|
|
|
activate.eq(1),
|
|
|
|
dfi.phases[0].ras_n.eq(0),
|
|
|
|
dfi.phases[0].cas_n.eq(1),
|
|
|
|
dfi.phases[0].we_n.eq(1),
|
2015-04-13 10:19:55 -04:00
|
|
|
NextState("TRCD"),
|
|
|
|
)
|
|
|
|
fsm.act("REFRESH",
|
2015-05-29 06:26:34 -04:00
|
|
|
refresh.eq(1),
|
2015-04-13 10:19:55 -04:00
|
|
|
dfi.phases[rdphase].ras_n.eq(0),
|
|
|
|
dfi.phases[rdphase].cas_n.eq(0),
|
|
|
|
dfi.phases[rdphase].we_n.eq(1),
|
|
|
|
NextState("POST-REFRESH")
|
|
|
|
)
|
2015-06-02 13:35:00 -04:00
|
|
|
fsm.delayed_enter("WRITE-LATENCY", "WRITE-ACK", phy_settings.write_latency-1)
|
2015-04-13 10:19:55 -04:00
|
|
|
fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1)
|
|
|
|
fsm.delayed_enter("TRCD", "IDLE", timing_settings.tRCD-1)
|
2015-06-02 13:35:00 -04:00
|
|
|
fsm.delayed_enter("PRE-REFRESH", "REFRESH", timing_settings.tRP-1)
|
2015-04-13 10:19:55 -04:00
|
|
|
fsm.delayed_enter("POST-REFRESH", "IDLE", timing_settings.tRFC-1)
|
2015-05-29 06:26:34 -04:00
|
|
|
|
|
|
|
# DFI commands
|
|
|
|
for phase in dfi.phases:
|
|
|
|
if hasattr(phase, "reset_n"):
|
|
|
|
self.comb += phase.reset_n.eq(1)
|
|
|
|
if hasattr(phase, "odt"):
|
|
|
|
self.comb += phase.odt.eq(1)
|
|
|
|
self.comb += [
|
|
|
|
phase.cke.eq(1),
|
|
|
|
phase.cs_n.eq(0),
|
|
|
|
phase.bank.eq(slicer.bank(bus.adr)),
|
|
|
|
If(precharge_all,
|
|
|
|
phase.address.eq(2**10)
|
|
|
|
).Elif(activate,
|
|
|
|
phase.address.eq(slicer.row(bus.adr))
|
|
|
|
).Elif(write | read,
|
|
|
|
phase.address.eq(slicer.col(bus.adr))
|
|
|
|
)
|
|
|
|
]
|
|
|
|
|
|
|
|
# DFI datapath
|
|
|
|
self.comb += [
|
|
|
|
bus.dat_r.eq(Cat(phase.rddata for phase in dfi.phases)),
|
|
|
|
Cat(phase.wrdata for phase in dfi.phases).eq(bus.dat_w),
|
|
|
|
Cat(phase.wrdata_mask for phase in dfi.phases).eq(~bus.sel),
|
|
|
|
]
|