Refresh Manager - several improvements
Considering initial PRE time for planning next REF when necessary. Postpone burst shall not be interrupted. Correct number of additional REF are pulled-in/postponed. Alignment to tREFI.
This commit is contained in:
@@ -16,6 +16,6 @@
|
||||
In library mode e.g. in Platform Architect the trace setup is ignored.
|
||||
-->
|
||||
<tracesetup>
|
||||
<device clkMhz="1000">ddr3_postpone_ref_test_3.stl</device>
|
||||
<device clkMhz="1000">ddr3_postpone_ref_test_1.stl</device>
|
||||
</tracesetup>
|
||||
</simulation>
|
||||
|
||||
@@ -45,24 +45,27 @@
|
||||
|
||||
using namespace tlm;
|
||||
|
||||
RefreshManager::RefreshManager(sc_module_name, ControllerCore &controller) :
|
||||
controllerCore(controller),
|
||||
timing(controller.config.memSpec.refreshTimings[Bank(0)]),
|
||||
nextPlannedRefresh(SC_ZERO_TIME)
|
||||
RefreshManager::RefreshManager(sc_module_name,
|
||||
ControllerCore &controller) : controllerCore(controller),
|
||||
timing(controller.config.memSpec.refreshTimings[Bank(0)])
|
||||
{
|
||||
auto m = controllerCore.config.getRefMode();
|
||||
tREFIx = timing.tREFI / m;
|
||||
tRFCx = m == 4 ? timing.tRFC4 : m == 2 ? timing.tRFC2 : timing.tRFC;
|
||||
if (controllerCore.config.ControllerCoreRefEnablePostpone) {
|
||||
maxpostpone = controllerCore.config.ControllerCoreRefMaxPostponed * m;
|
||||
}
|
||||
if (controllerCore.config.ControllerCoreRefEnablePullIn) {
|
||||
maxpullin = controllerCore.config.ControllerCoreRefMaxPulledIn * m;
|
||||
}
|
||||
postponeEnabled = controllerCore.config.ControllerCoreRefEnablePostpone;
|
||||
pullInEnabled = controllerCore.config.ControllerCoreRefEnablePullIn;
|
||||
maxpostpone = controllerCore.config.ControllerCoreRefMaxPostponed * m;
|
||||
maxpullin = controllerCore.config.ControllerCoreRefMaxPulledIn * m;
|
||||
pulledin = 0;
|
||||
postponed = 0;
|
||||
currentState = ST_REFRESH;
|
||||
previousState = ST_REFRESH;
|
||||
nextState = ST_REFRESH;
|
||||
nextPlannedRefresh = SC_ZERO_TIME;
|
||||
for (Bank bank : controller.getBanks()) {
|
||||
setUpDummy(refreshPayloads[bank], bank);
|
||||
}
|
||||
planNextRefresh(tREFIx);
|
||||
planNextRefresh(tREFIx, false);
|
||||
}
|
||||
|
||||
RefreshManager::~RefreshManager()
|
||||
@@ -75,23 +78,27 @@ bool RefreshManager::hasCollision(const ScheduledCommand &command)
|
||||
bool collisionWithPreviousRefEnd = command.getStart() <
|
||||
controllerCore.state->getLastCommand(Command::AutoRefresh).getEnd();
|
||||
bool collisionWithNextRefStart = command.getEnd() >= nextPlannedRefresh;
|
||||
|
||||
if (controllerCore.config.ControllerCoreRefEnablePostpone
|
||||
&& (arCmdCounter < maxpostpone)) {
|
||||
// Flexible refresh is on and have "credits" to postpone
|
||||
&& (postponed < maxpostpone)) {
|
||||
// Flexible refresh is on and have "credits" to postpone.
|
||||
// Then there will not be a collision with next refresh because
|
||||
// nextPlannedRefresh will be updated
|
||||
// nextPlannedRefresh will be updated.
|
||||
collisionWithNextRefStart = false;
|
||||
}
|
||||
if (currentState == ST_BURST) {
|
||||
// A burst due to postponed refreshes shall not be interrupted.
|
||||
collisionWithNextRefStart = true;
|
||||
}
|
||||
return collisionWithPreviousRefEnd || collisionWithNextRefStart;
|
||||
}
|
||||
|
||||
void RefreshManager::doRefresh(tlm::tlm_generic_payload &payload, sc_time time)
|
||||
bool RefreshManager::doRefresh(tlm::tlm_generic_payload &payload, sc_time time)
|
||||
{
|
||||
sc_assert(!isInvalidated(payload, time));
|
||||
//Check if any row on all banks is activated and if so, a PrechargeAll
|
||||
//command must be scheduled before the refresh command.
|
||||
bool pre = false;
|
||||
// If any row is open, precharge all.
|
||||
if (!controllerCore.state->rowBufferStates->allRowBuffersAreClosed()) {
|
||||
pre = true;
|
||||
ScheduledCommand prechargeAllMaster(Command::PrechargeAll, time,
|
||||
getExecutionTime(Command::PrechargeAll, refreshPayloads[Bank(0)]),
|
||||
refreshPayloads[Bank(0)]);
|
||||
@@ -124,6 +131,7 @@ void RefreshManager::doRefresh(tlm::tlm_generic_payload &payload, sc_time time)
|
||||
controllerCore.state->change(refreshAllMaster);
|
||||
DramExtension::getExtension(refreshPayloads[Bank(0)]).incrementRow();
|
||||
controllerCore.controller.send(refreshAllMaster, refreshPayloads[Bank(0)]);
|
||||
return pre;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,23 +140,29 @@ void RefreshManager::scheduleRefresh(tlm::tlm_generic_payload &payload,
|
||||
{
|
||||
sc_time nextRefTiming;
|
||||
bool pendingReq = controllerCore.hasPendingRequests();
|
||||
bool canPostpone = pendingReq && (arCmdCounter < maxpostpone);
|
||||
bool canPullIn = !pendingReq && (arCmdCounter < maxpullin);
|
||||
bool canPostpone = postponeEnabled && pendingReq && (postponed < maxpostpone);
|
||||
bool canPullIn = pullInEnabled && !pendingReq && (pulledin < maxpullin);
|
||||
previousState = currentState;
|
||||
currentState = nextState;
|
||||
bool align = false;
|
||||
sc_time nrt;
|
||||
bool pre;
|
||||
|
||||
switch (currentState) {
|
||||
case ST_REFRESH:
|
||||
// Regular Refresh. It's possible to migrate from this to the flexible
|
||||
// refresh states
|
||||
// The arCmdCounter should always be equal to zero here
|
||||
assert(arCmdCounter == 0);
|
||||
assert(pulledin == 0 && postponed == 0);
|
||||
if (canPostpone) {
|
||||
nextState = ST_POSTPONE;
|
||||
nextRefTiming = SC_ZERO_TIME;
|
||||
nextState = ST_POSTPONE;
|
||||
} else if (canPullIn) {
|
||||
pre = doRefresh(payload, time);
|
||||
nrt = tRFCx;
|
||||
if (pre)
|
||||
nrt += controllerCore.config.memSpec.tRP;
|
||||
nextRefTiming = nrt;
|
||||
nextState = ST_PULLIN;
|
||||
nextRefTiming = SC_ZERO_TIME; // Attempt to burst pull-in
|
||||
} else {
|
||||
doRefresh(payload, time);
|
||||
nextRefTiming = tREFIx;
|
||||
@@ -156,89 +170,86 @@ void RefreshManager::scheduleRefresh(tlm::tlm_generic_payload &payload,
|
||||
}
|
||||
break;
|
||||
case ST_PULLIN:
|
||||
// Pull-In Refresh. Try to pull-in refreshes as long as the limit
|
||||
// hasn't been reached yet and has credits
|
||||
// Pull-in a refresh. Try to pull-in refreshes as long it is possible.
|
||||
if (canPullIn) {
|
||||
doRefresh(payload, time);
|
||||
arCmdCounter++;
|
||||
pulledin++;
|
||||
pre = doRefresh(payload, time);
|
||||
nrt = tRFCx;
|
||||
if (pre)
|
||||
nrt += controllerCore.config.memSpec.tRP;
|
||||
nextRefTiming = nrt;
|
||||
nextState = ST_PULLIN;
|
||||
nextRefTiming = tRFCx;
|
||||
} else {
|
||||
// Saving value to be used by ST_ALIGN
|
||||
alignValue = arCmdCounter;
|
||||
nextState = ST_ALIGN;
|
||||
nextRefTiming = SC_ZERO_TIME;
|
||||
nextState = ST_ALIGN;
|
||||
}
|
||||
break;
|
||||
case ST_SKIP:
|
||||
// Skip Refresh. The arCmdCounter is used to skip the correct amount
|
||||
// of refreshes
|
||||
arCmdCounter--;
|
||||
if (arCmdCounter == 0) {
|
||||
nextState = ST_REFRESH;
|
||||
// Skip the pulled-in refreshes.
|
||||
if (pulledin == 0) {
|
||||
nextRefTiming = SC_ZERO_TIME;
|
||||
nextState = ST_REFRESH;
|
||||
} else {
|
||||
nextState = ST_SKIP;
|
||||
pulledin--;
|
||||
nextRefTiming = tREFIx;
|
||||
nextState = ST_SKIP;
|
||||
}
|
||||
break;
|
||||
case ST_POSTPONE:
|
||||
// Postpone Refresh. Delaying refreshes as long as there are pending
|
||||
// requests and credits to postpone. Should be followed by a burst
|
||||
// refresh
|
||||
if ((arCmdCounter == maxpostpone) || ((!pendingReq)
|
||||
&& !controllerCore.config.ControllerCoreRefForceMaxPostponeBurst)) {
|
||||
// Burst conditions met
|
||||
if (arCmdCounter < maxpostpone) {
|
||||
// In case the burst was started by inactivity, need to also
|
||||
// count the current REF
|
||||
arCmdCounter++;
|
||||
}
|
||||
// Will start a burst next, so the value is saved to be used by
|
||||
// ST_ALIGN
|
||||
alignValue = arCmdCounter;
|
||||
nextState = ST_BURST;
|
||||
// refresh.
|
||||
postponed++;
|
||||
if ((postponed > maxpostpone) || (!pendingReq
|
||||
&& !controllerCore.config.ControllerCoreRefForceMaxPostponeBurst)) {
|
||||
// Burst triggered by inactivity or max postpone value reached.
|
||||
nextRefTiming = SC_ZERO_TIME;
|
||||
nextState = ST_BURST;
|
||||
} else {
|
||||
arCmdCounter++;
|
||||
nextState = ST_POSTPONE;
|
||||
nextRefTiming = tREFIx;
|
||||
nextState = ST_POSTPONE;
|
||||
}
|
||||
break;
|
||||
case ST_BURST:
|
||||
// Burst Refresh. The arCmdCounter is used to issue the correct amount
|
||||
// of refreshes
|
||||
arCmdCounter--;
|
||||
doRefresh(payload, time);
|
||||
if (arCmdCounter == 0) {
|
||||
// All bursts issued, next state will align to tREFIx
|
||||
nextState = ST_ALIGN;
|
||||
// Burst postponed refreshes.
|
||||
pre = doRefresh(payload, time);
|
||||
postponed--;
|
||||
if (postponed == 0) {
|
||||
// All refreshes issued, next state will align to tREFIx
|
||||
nextRefTiming = SC_ZERO_TIME;
|
||||
nextState = ST_ALIGN;
|
||||
} else {
|
||||
nrt = tRFCx;
|
||||
if (pre)
|
||||
nrt += controllerCore.config.memSpec.tRP;
|
||||
nextRefTiming = nrt;
|
||||
nextState = ST_BURST;
|
||||
nextRefTiming = tRFCx;
|
||||
}
|
||||
break;
|
||||
case ST_ALIGN:
|
||||
// Align Refresh. Adjusting the timing so the next REF timing will be
|
||||
// a in a time multiple of tREFIx
|
||||
nextRefTiming = tREFIx;
|
||||
align = true;
|
||||
if (previousState == ST_PULLIN) {
|
||||
nextRefTiming = tREFIx - (tRFCx * (alignValue));
|
||||
nextState = ST_SKIP;
|
||||
} else {
|
||||
nextRefTiming = tREFIx - (tRFCx * (alignValue - 1));
|
||||
nextState = ST_REFRESH;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SC_REPORT_FATAL(this->name(), "Invalid state in flexible refresh FSM. Stop.");
|
||||
SC_REPORT_FATAL(this->name(), "Invalid state in flexible refresh FSM.");
|
||||
break;
|
||||
}
|
||||
planNextRefresh(nextRefTiming);
|
||||
planNextRefresh(nextRefTiming, align);
|
||||
}
|
||||
|
||||
void RefreshManager::planNextRefresh(sc_time nextRefTiming)
|
||||
void RefreshManager::planNextRefresh(sc_time nextRefTiming, bool align)
|
||||
{
|
||||
if (align) {
|
||||
nextPlannedRefresh = trunc(nextPlannedRefresh.to_double() / tREFIx.to_double())
|
||||
* tREFIx;
|
||||
}
|
||||
nextPlannedRefresh += nextRefTiming;
|
||||
controllerCore.controller.send(REFTrigger, nextPlannedRefresh,
|
||||
refreshPayloads[Bank(0)]);
|
||||
@@ -247,7 +258,7 @@ void RefreshManager::planNextRefresh(sc_time nextRefTiming)
|
||||
void RefreshManager::reInitialize(Bank, sc_time time)
|
||||
{
|
||||
nextPlannedRefresh = clkAlign(time, Alignment::DOWN);
|
||||
planNextRefresh(tREFIx);
|
||||
planNextRefresh(tREFIx, true);
|
||||
}
|
||||
|
||||
bool RefreshManager::isInvalidated(tlm::tlm_generic_payload &payload
|
||||
|
||||
@@ -63,17 +63,18 @@ private:
|
||||
sc_time tREFIx;
|
||||
sc_time tRFCx;
|
||||
std::map<Bank, tlm::tlm_generic_payload> refreshPayloads;
|
||||
unsigned int maxpostpone = 0;
|
||||
unsigned int maxpullin = 0;
|
||||
unsigned int alignToTrefiValue = 0;
|
||||
unsigned int arCmdCounter = 0;
|
||||
unsigned int alignValue = 0;
|
||||
ref_fsm_state_t currentState = ST_REFRESH;
|
||||
ref_fsm_state_t previousState = ST_REFRESH;
|
||||
ref_fsm_state_t nextState = ST_REFRESH;
|
||||
bool postponeEnabled;
|
||||
bool pullInEnabled;
|
||||
unsigned int maxpostpone;
|
||||
unsigned int maxpullin;
|
||||
unsigned int pulledin;
|
||||
unsigned int postponed;
|
||||
ref_fsm_state_t currentState;
|
||||
ref_fsm_state_t previousState;
|
||||
ref_fsm_state_t nextState;
|
||||
|
||||
void doRefresh(tlm::tlm_generic_payload &payload, sc_time time);
|
||||
void planNextRefresh(sc_time time);
|
||||
bool doRefresh(tlm::tlm_generic_payload &payload, sc_time time);
|
||||
void planNextRefresh(sc_time time, bool align);
|
||||
void printDebugMessage(std::string message);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user