lpddr4: implement ZQC through MPC and include it in init sequence
We do not yet support ZQC during operation (after init sequence) as LPDDR4 requires 2-stage ZQC (start+latch) and 1us in between, which requires modifying Refresher (ZQCExecutor) in LiteDRAM.
This commit is contained in:
parent
055e2dc597
commit
ee9c2b4cf7
|
@ -554,11 +554,16 @@ def get_lpddr4_phy_init_sequence(phy_settings, timing_settings):
|
||||||
ba = 0
|
ba = 0
|
||||||
return ("Load More Register {}".format(ma), a, ba, cmds["MODE_REGISTER"], 200)
|
return ("Load More Register {}".format(ma), a, ba, cmds["MODE_REGISTER"], 200)
|
||||||
|
|
||||||
|
from litedram.phy.lpddr4phy import DFIPhaseAdapter
|
||||||
|
zqc_start = DFIPhaseAdapter.MPC["ZQC-START"]
|
||||||
|
zqc_latch = DFIPhaseAdapter.MPC["ZQC-LATCH"]
|
||||||
|
|
||||||
init_sequence = [
|
init_sequence = [
|
||||||
("Release reset", 0x0000, 0, cmds["UNRESET"], 50000),
|
("Release reset", 0x0000, 0, cmds["UNRESET"], 50000),
|
||||||
("Bring CKE high", 0x0000, 0, cmds["CKE"], 10000),
|
("Bring CKE high", 0x0000, 0, cmds["CKE"], 10000),
|
||||||
*[cmd_mr(ma) for ma in sorted(mr.keys())],
|
*[cmd_mr(ma) for ma in sorted(mr.keys())],
|
||||||
# TODO: ZQ calibration
|
("ZQ Calibration start", zqc_start, 0, "DFII_COMMAND_WE|DFII_COMMAND_CS", 1000), # > tZQCAL=1us
|
||||||
|
("ZQ Calibration latch", zqc_latch, 0, "DFII_COMMAND_WE|DFII_COMMAND_CS", 200), # > tZQLAT=max(8ck, 30ns)
|
||||||
]
|
]
|
||||||
|
|
||||||
return init_sequence, mr
|
return init_sequence, mr
|
||||||
|
|
|
@ -974,7 +974,10 @@ class MT53E256M16D1(SDRAMModule):
|
||||||
nrows = 32768
|
nrows = 32768
|
||||||
ncols = 1024
|
ncols = 1024
|
||||||
|
|
||||||
technology_timings = _TechnologyTimings(tREFI=32e6/8192, tWTR=(8, 10), tCCD=(8, None), tRRD=(4, 10), tZQCS=None) # TODO: tZQCS
|
# TODO: tZQCS - performing ZQC during runtime will require modifying Refresher, as ZQC has to be done in 2 phases
|
||||||
|
# 1. ZQCAL START is issued 2. ZQCAL LATCH updates the values, the time START->LATCH tZQCAL=1us, so we cannot block
|
||||||
|
# the controller during this time, after ZQCAL LATCH we have to wait tZQLAT=max(8ck, 30ns)
|
||||||
|
technology_timings = _TechnologyTimings(tREFI=32e6/8192, tWTR=(8, 10), tCCD=(8, None), tRRD=(4, 10), tZQCS=None)
|
||||||
speedgrade_timings = {
|
speedgrade_timings = {
|
||||||
"1866": _SpeedgradeTimings(tRP=(3, 21), tRCD=(4, 18), tWR=(4, 18), tRFC=180, tFAW=40, tRAS=(3, 42)), # TODO: tRAS_max
|
"1866": _SpeedgradeTimings(tRP=(3, 21), tRCD=(4, 18), tWR=(4, 18), tRFC=180, tFAW=40, tRAS=(3, 42)), # TODO: tRAS_max
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,6 +384,22 @@ class DFIPhaseAdapter(Module):
|
||||||
# Then most "big commands" consist of 2 "small commands" (e.g. ACTIVATE-1, ACTIVATE-2).
|
# Then most "big commands" consist of 2 "small commands" (e.g. ACTIVATE-1, ACTIVATE-2).
|
||||||
# If a command uses 1 "small command", then it shall go as cmd2 so that all command
|
# If a command uses 1 "small command", then it shall go as cmd2 so that all command
|
||||||
# timings can be counted from the same moment (cycle of cmd2 CS low).
|
# timings can be counted from the same moment (cycle of cmd2 CS low).
|
||||||
|
|
||||||
|
# MPC (multipurpose command) can be used to perform different actions
|
||||||
|
# We use ZQC with BA=0 to issue MPC, where OP[6:0] = A[6:0]
|
||||||
|
MPC = {
|
||||||
|
"NOP": 0b0000000, # only OP[6] must be 0
|
||||||
|
"READ-FIFO": 0b1000001,
|
||||||
|
"READ-DQ-CAL": 0b1000011,
|
||||||
|
# RFU: 0b1000101
|
||||||
|
"WRITE-FIFO": 0b1000111,
|
||||||
|
# RFU: 0b1001001
|
||||||
|
"START-DQS-OSC": 0b1001011,
|
||||||
|
"STOP-DQS-OSC": 0b1001101,
|
||||||
|
"ZQC-START": 0b1001111,
|
||||||
|
"ZQC-LATCH": 0b1010001,
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, dfi_phase):
|
def __init__(self, dfi_phase):
|
||||||
# CS/CA values for 4 SDR cycles
|
# CS/CA values for 4 SDR cycles
|
||||||
self.cs = Signal(4)
|
self.cs = Signal(4)
|
||||||
|
@ -426,11 +442,7 @@ class DFIPhaseAdapter(Module):
|
||||||
_cmd["WR"]: cmds("WRITE-1", "CAS-2"), # TODO: masked write
|
_cmd["WR"]: cmds("WRITE-1", "CAS-2"), # TODO: masked write
|
||||||
_cmd["PRE"]: cmds("DESELECT", "PRECHARGE"),
|
_cmd["PRE"]: cmds("DESELECT", "PRECHARGE"),
|
||||||
_cmd["REF"]: cmds("DESELECT", "REFRESH"),
|
_cmd["REF"]: cmds("DESELECT", "REFRESH"),
|
||||||
# TODO: ZQC init/short/long? start/latch?
|
_cmd["ZQC"]: cmds("DESELECT", "MPC"),
|
||||||
# _cmd["ZQC"]: [
|
|
||||||
# *cmds("DESELECT", "MPC"),
|
|
||||||
# self.cmd2.mpc.eq(0b1001111),
|
|
||||||
# ],
|
|
||||||
_cmd["MRS"]: cmds("MRW-1", "MRW-2"),
|
_cmd["MRS"]: cmds("MRW-1", "MRW-2"),
|
||||||
"default": cmds("DESELECT", "DESELECT", valid=0),
|
"default": cmds("DESELECT", "DESELECT", valid=0),
|
||||||
})
|
})
|
||||||
|
@ -462,7 +474,6 @@ class Command(Module):
|
||||||
def __init__(self, dfi_phase):
|
def __init__(self, dfi_phase):
|
||||||
self.cs = Signal(2)
|
self.cs = Signal(2)
|
||||||
self.ca = Array([Signal(6), Signal(6)]) # CS high, CS low
|
self.ca = Array([Signal(6), Signal(6)]) # CS high, CS low
|
||||||
self.mpc = Signal(7) # special OP values for multipurpose command
|
|
||||||
self.dfi = dfi_phase
|
self.dfi = dfi_phase
|
||||||
|
|
||||||
def set(self, cmd):
|
def set(self, cmd):
|
||||||
|
@ -487,8 +498,7 @@ class Command(Module):
|
||||||
"R(\d+)": lambda i: self.dfi.address[i], # row
|
"R(\d+)": lambda i: self.dfi.address[i], # row
|
||||||
"C(\d+)": lambda i: self.dfi.address[i], # column
|
"C(\d+)": lambda i: self.dfi.address[i], # column
|
||||||
"MA(\d+)": lambda i: self.dfi.address[8+i], # mode register address
|
"MA(\d+)": lambda i: self.dfi.address[8+i], # mode register address
|
||||||
# mode register value, or op code for MPC
|
"OP(\d+)": lambda i: self.dfi.address[i], # mode register value, or operand for MPC
|
||||||
"OP(\d+)": lambda i: self.mpc[i] if is_mpc else self.dfi.address[i],
|
|
||||||
}
|
}
|
||||||
for pattern, value in rules.items():
|
for pattern, value in rules.items():
|
||||||
m = re.match(pattern, bit)
|
m = re.match(pattern, bit)
|
||||||
|
|
|
@ -506,22 +506,25 @@ class TestLPDDR4(unittest.TestCase):
|
||||||
refresh_ab = dict(cs_n=0, cas_n=0, ras_n=0, we_n=1, bank=0b100, address=0b10000000000)
|
refresh_ab = dict(cs_n=0, cas_n=0, ras_n=0, we_n=1, bank=0b100, address=0b10000000000)
|
||||||
precharge = dict(cs_n=0, cas_n=1, ras_n=0, we_n=0, bank=0b011, address=0)
|
precharge = dict(cs_n=0, cas_n=1, ras_n=0, we_n=0, bank=0b011, address=0)
|
||||||
mrw = dict(cs_n=0, cas_n=0, ras_n=0, we_n=0, bank=0, address=(0b110011 << 8) | 0b10101010) # 6-bit address | 8-bit op code
|
mrw = dict(cs_n=0, cas_n=0, ras_n=0, we_n=0, bank=0, address=(0b110011 << 8) | 0b10101010) # 6-bit address | 8-bit op code
|
||||||
|
zqc_start = dict(cs_n=0, cas_n=1, ras_n=1, we_n=0, bank=0, address=0b1001111) # MPC with ZQCAL START operand
|
||||||
|
zqc_latch = dict(cs_n=0, cas_n=1, ras_n=1, we_n=0, bank=0, address=0b1010001) # MPC with ZQCAL LATCH operand
|
||||||
self.run_test(SimulationPHY(),
|
self.run_test(SimulationPHY(),
|
||||||
dfi_sequence = [
|
dfi_sequence = [
|
||||||
{0: read, 4: write_ap},
|
{0: read, 4: write_ap},
|
||||||
{0: activate, 4: refresh_ab},
|
{0: activate, 4: refresh_ab},
|
||||||
{0: precharge, 4: mrw},
|
{0: precharge, 4: mrw},
|
||||||
|
{0: zqc_start, 4: zqc_latch},
|
||||||
],
|
],
|
||||||
pad_checkers = {"sys8x_90": {
|
pad_checkers = {"sys8x_90": {
|
||||||
# note that refresh and precharge have a single command so these go as cmd2
|
# note that refresh and precharge have a single command so these go as cmd2
|
||||||
# rd wr act ref pre mrw
|
# rd wr act ref pre mrw zqcs zqcl
|
||||||
'cs': latency + '1010'+'1010' + '1010'+'0010' + '0010'+'1010',
|
'cs': latency + '1010'+'1010' + '1010'+'0010' + '0010'+'1010' + '0010'+'0010',
|
||||||
'ca0': latency + '0100'+'0100' + '1011'+'0000' + '0001'+'0100',
|
'ca0': latency + '0100'+'0100' + '1011'+'0000' + '0001'+'0100' + '0001'+'0001',
|
||||||
'ca1': latency + '1010'+'0110' + '0110'+'0000' + '0001'+'1111',
|
'ca1': latency + '1010'+'0110' + '0110'+'0000' + '0001'+'1111' + '0001'+'0000',
|
||||||
'ca2': latency + '0101'+'1100' + '0010'+'0001' + '0000'+'1010',
|
'ca2': latency + '0101'+'1100' + '0010'+'0001' + '0000'+'1010' + '0001'+'0000',
|
||||||
'ca3': latency + '0x01'+'0x00' + '1110'+'001x' + '000x'+'0001',
|
'ca3': latency + '0x01'+'0x00' + '1110'+'001x' + '000x'+'0001' + '0001'+'0000',
|
||||||
'ca4': latency + '0110'+'0010' + '1010'+'000x' + '001x'+'0110',
|
'ca4': latency + '0110'+'0010' + '1010'+'000x' + '001x'+'0110' + '0000'+'0001',
|
||||||
'ca5': latency + '0010'+'0100' + '1001'+'001x' + '000x'+'1101',
|
'ca5': latency + '0010'+'0100' + '1001'+'001x' + '000x'+'1101' + '0010'+'0010',
|
||||||
}},
|
}},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue