mem: Update DRAM command scheduler for bank groups

This patch updates the command arbitration so that bank group timing
as well as rank-to-rank delays will be taken into account. The
resulting arbitration no longer selects commands (prepped or not) that
cannot issue seamlessly if there are commands that can issue
back-to-back, minimizing the effect of rank-to-rank (tCS) & same bank
group (tCCD_L) delays.

The arbitration selects a new command based on the following priority.
Within each priority band, the arbitration will use FCFS to select the
appropriate command:

1) Bank is prepped and burst can issue seamlessly, without a bubble

2) Bank is not prepped, but can prep and issue seamlessly, without a
bubble

3) Bank is prepped but burst cannot issue seamlessly. In this case, a
bubble will occur on the bus

Thus, to enable more parallelism in subsequent selections, an
unprepped packet is given higher priority if the bank prep can be
hidden. If the bank prep cannot be hidden, the selection logic will
choose a prepped packet that cannot issue seamlessly if one exist.
Otherwise, the default selection will choose the packet with the
minimum bank prep delay.
This commit is contained in:
Wendy Elsasser 2015-07-03 10:14:46 -04:00
parent b56167b682
commit 31f901b69d
2 changed files with 133 additions and 118 deletions

View file

@ -711,7 +711,7 @@ DRAMCtrl::processRespondEvent()
}
bool
DRAMCtrl::chooseNext(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
DRAMCtrl::chooseNext(std::deque<DRAMPacket*>& queue, Tick extra_col_delay)
{
// This method does the arbitration between requests. The chosen
// packet is simply moved to the head of the queue. The other
@ -745,68 +745,93 @@ DRAMCtrl::chooseNext(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
}
}
} else if (memSchedPolicy == Enums::frfcfs) {
found_packet = reorderQueue(queue, switched_cmd_type);
found_packet = reorderQueue(queue, extra_col_delay);
} else
panic("No scheduling policy chosen\n");
return found_packet;
}
bool
DRAMCtrl::reorderQueue(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
DRAMCtrl::reorderQueue(std::deque<DRAMPacket*>& queue, Tick extra_col_delay)
{
// Only determine this when needed
// Only determine this if needed
uint64_t earliest_banks = 0;
bool hidden_bank_prep = false;
// Search for row hits first, if no row hit is found then schedule the
// packet to one of the earliest banks available
bool found_packet = false;
// search for seamless row hits first, if no seamless row hit is
// found then determine if there are other packets that can be issued
// without incurring additional bus delay due to bank timing
// Will select closed rows first to enable more open row possibilies
// in future selections
bool found_hidden_bank = false;
// remember if we found a row hit, not seamless, but bank prepped
// and ready
bool found_prepped_pkt = false;
// if we have no row hit, prepped or not, and no seamless packet,
// just go for the earliest possible
bool found_earliest_pkt = false;
bool found_prepped_diff_rank_pkt = false;
auto selected_pkt_it = queue.end();
// time we need to issue a column command to be seamless
const Tick min_col_at = std::max(busBusyUntil - tCL + extra_col_delay,
curTick());
for (auto i = queue.begin(); i != queue.end() ; ++i) {
DRAMPacket* dram_pkt = *i;
const Bank& bank = dram_pkt->bankRef;
// check if rank is busy. If this is the case jump to the next packet
// Check if it is a row hit
if (dram_pkt->rankRef.isAvailable()) {
if (bank.openRow == dram_pkt->row) {
if (dram_pkt->rank == activeRank || switched_cmd_type) {
// FCFS within the hits, giving priority to commands
// that access the same rank as the previous burst
// to minimize bus turnaround delays
// Only give rank prioity when command type is
// not changing
DPRINTF(DRAM, "Row buffer hit\n");
selected_pkt_it = i;
break;
} else if (!found_prepped_diff_rank_pkt) {
// found row hit for command on different rank
// than prev burst
selected_pkt_it = i;
found_prepped_diff_rank_pkt = true;
}
} else if (!found_earliest_pkt & !found_prepped_diff_rank_pkt) {
// packet going to a rank which is currently not waiting for a
// refresh, No row hit and
// haven't found an entry with a row hit to a new rank
if (earliest_banks == 0)
// Determine entries with earliest bank prep delay
// Function will give priority to commands that access the
// same rank as previous burst and can prep
// the bank seamlessly
earliest_banks = minBankPrep(queue, switched_cmd_type);
// FCFS - Bank is first available bank
if (bits(earliest_banks, dram_pkt->bankId,
dram_pkt->bankId)) {
// Remember the packet to be scheduled to one of
// the earliest banks available, FCFS amongst the
// earliest banks
// check if rank is available, if not, jump to the next packet
if (dram_pkt->rankRef.isAvailable()) {
// check if it is a row hit
if (bank.openRow == dram_pkt->row) {
// no additional rank-to-rank or same bank-group
// delays, or we switched read/write and might as well
// go for the row hit
if (bank.colAllowedAt <= min_col_at) {
// FCFS within the hits, giving priority to
// commands that can issue seamlessly, without
// additional delay, such as same rank accesses
// and/or different bank-group accesses
DPRINTF(DRAM, "Seamless row buffer hit\n");
selected_pkt_it = i;
//if the packet found is going to a rank that is currently
//not busy then update the found_packet to true
// no need to look through the remaining queue entries
break;
} else if (!found_hidden_bank && !found_prepped_pkt) {
// if we did not find a packet to a closed row that can
// issue the bank commands without incurring delay, and
// did not yet find a packet to a prepped row, remember
// the current one
selected_pkt_it = i;
found_prepped_pkt = true;
DPRINTF(DRAM, "Prepped row buffer hit\n");
}
} else if (!found_earliest_pkt) {
// if we have not initialised the bank status, do it
// now, and only once per scheduling decisions
if (earliest_banks == 0) {
// determine entries with earliest bank delay
pair<uint64_t, bool> bankStatus =
minBankPrep(queue, min_col_at);
earliest_banks = bankStatus.first;
hidden_bank_prep = bankStatus.second;
}
// bank is amongst first available banks
// minBankPrep will give priority to packets that can
// issue seamlessly
if (bits(earliest_banks, dram_pkt->bankId, dram_pkt->bankId)) {
found_earliest_pkt = true;
found_hidden_bank = hidden_bank_prep;
// give priority to packets that can issue
// bank commands 'behind the scenes'
// any additional delay if any will be due to
// col-to-col command requirements
if (hidden_bank_prep || !found_prepped_pkt)
selected_pkt_it = i;
}
}
}
@ -816,9 +841,10 @@ DRAMCtrl::reorderQueue(std::deque<DRAMPacket*>& queue, bool switched_cmd_type)
DRAMPacket* selected_pkt = *selected_pkt_it;
queue.erase(selected_pkt_it);
queue.push_front(selected_pkt);
found_packet = true;
return true;
}
return found_packet;
return false;
}
void
@ -1291,7 +1317,10 @@ DRAMCtrl::processNextReqEvent()
// Figure out which read request goes next, and move it to the
// front of the read queue
found_read = chooseNext(readQueue, switched_cmd_type);
// If we are changing command type, incorporate the minimum
// bus turnaround delay which will be tCS (different rank) case
found_read = chooseNext(readQueue,
switched_cmd_type ? tCS : 0);
// if no read to an available rank is found then return
// at this point. There could be writes to the available ranks
@ -1350,7 +1379,10 @@ DRAMCtrl::processNextReqEvent()
// bool to check if write to free rank is found
bool found_write = false;
found_write = chooseNext(writeQueue, switched_cmd_type);
// If we are changing command type, incorporate the minimum
// bus turnaround delay
found_write = chooseNext(writeQueue,
switched_cmd_type ? std::min(tRTW, tCS) : 0);
// if no writes to an available rank are found then return.
// There could be reads to the available ranks. However, to avoid
@ -1410,18 +1442,23 @@ DRAMCtrl::processNextReqEvent()
}
}
uint64_t
pair<uint64_t, bool>
DRAMCtrl::minBankPrep(const deque<DRAMPacket*>& queue,
bool switched_cmd_type) const
Tick min_col_at) const
{
uint64_t bank_mask = 0;
Tick min_act_at = MaxTick;
uint64_t bank_mask_same_rank = 0;
Tick min_act_at_same_rank = MaxTick;
// latest Tick for which ACT can occur without incurring additoinal
// delay on the data bus
const Tick hidden_act_max = std::max(min_col_at - tRCD, curTick());
// Give precedence to commands that access same rank as previous command
bool same_rank_match = false;
// Flag condition when burst can issue back-to-back with previous burst
bool found_seamless_bank = false;
// Flag condition when bank can be opened without incurring additional
// delay on the data bus
bool hidden_bank_prep = false;
// determine if we have queued transactions targetting the
// bank in question
@ -1431,6 +1468,8 @@ DRAMCtrl::minBankPrep(const deque<DRAMPacket*>& queue,
got_waiting[p->bankId] = true;
}
// Find command with optimal bank timing
// Will prioritize commands that can issue seamlessly.
for (int i = 0; i < ranksPerChannel; i++) {
for (int j = 0; j < banksPerRank; j++) {
uint16_t bank_id = i * banksPerRank + j;
@ -1444,69 +1483,46 @@ DRAMCtrl::minBankPrep(const deque<DRAMPacket*>& queue,
// an activate, ignoring any rank-to-rank switching
// cost in this calculation
Tick act_at = ranks[i]->banks[j].openRow == Bank::NO_ROW ?
ranks[i]->banks[j].actAllowedAt :
std::max(ranks[i]->banks[j].actAllowedAt, curTick()) :
std::max(ranks[i]->banks[j].preAllowedAt, curTick()) + tRP;
// prioritize commands that access the
// same rank as previous burst
// Calculate bank mask separately for the case and
// evaluate after loop iterations complete
if (i == activeRank && ranksPerChannel > 1) {
if (act_at <= min_act_at_same_rank) {
// reset same rank bank mask if new minimum is found
// and previous minimum could not immediately send ACT
if (act_at < min_act_at_same_rank &&
min_act_at_same_rank > curTick())
bank_mask_same_rank = 0;
// When is the earliest the R/W burst can issue?
Tick col_at = std::max(ranks[i]->banks[j].colAllowedAt,
act_at + tRCD);
// Set flag indicating that a same rank
// opportunity was found
same_rank_match = true;
// bank can issue burst back-to-back (seamlessly) with
// previous burst
bool new_seamless_bank = col_at <= min_col_at;
// set the bit corresponding to the available bank
replaceBits(bank_mask_same_rank, bank_id, bank_id, 1);
min_act_at_same_rank = act_at;
}
} else {
if (act_at <= min_act_at) {
// reset bank mask if new minimum is found
// and either previous minimum could not immediately send ACT
if (act_at < min_act_at && min_act_at > curTick())
bank_mask = 0;
// set the bit corresponding to the available bank
replaceBits(bank_mask, bank_id, bank_id, 1);
min_act_at = act_at;
// if we found a new seamless bank or we have no
// seamless banks, and got a bank with an earlier
// activate time, it should be added to the bit mask
if (new_seamless_bank ||
(!found_seamless_bank && act_at <= min_act_at)) {
// if we did not have a seamless bank before, and
// we do now, reset the bank mask, also reset it
// if we have not yet found a seamless bank and
// the activate time is smaller than what we have
// seen so far
if (!found_seamless_bank &&
(new_seamless_bank || act_at < min_act_at)) {
bank_mask = 0;
}
found_seamless_bank |= new_seamless_bank;
// ACT can occur 'behind the scenes'
hidden_bank_prep = act_at <= hidden_act_max;
// set the bit corresponding to the available bank
replaceBits(bank_mask, bank_id, bank_id, 1);
min_act_at = act_at;
}
}
}
}
// Determine the earliest time when the next burst can issue based
// on the current busBusyUntil delay.
// Offset by tRCD to correlate with ACT timing variables
Tick min_cmd_at = busBusyUntil - tCL - tRCD;
// if we have multiple ranks and all
// waiting packets are accessing a rank which was previously active
// then bank_mask_same_rank will be set to a value while bank_mask will
// remain 0. In this case, the function should return the value of
// bank_mask_same_rank.
// else if waiting packets access a rank which was previously active and
// other ranks, prioritize same rank accesses that can issue B2B
// Only optimize for same ranks when the command type
// does not change; do not want to unnecessarily incur tWTR
//
// Resulting FCFS prioritization Order is:
// 1) Commands that access the same rank as previous burst
// and can prep the bank seamlessly.
// 2) Commands (any rank) with earliest bank prep
if ((bank_mask == 0) || (!switched_cmd_type && same_rank_match &&
min_act_at_same_rank <= min_cmd_at)) {
bank_mask = bank_mask_same_rank;
}
return bank_mask;
return make_pair(bank_mask, hidden_bank_prep);
}
DRAMCtrl::Rank::Rank(DRAMCtrl& _memory, const DRAMCtrlParams* _p)

View file

@ -574,24 +574,22 @@ class DRAMCtrl : public AbstractMemory
* controller is switching command type.
*
* @param queue Queued requests to consider
* @param switched_cmd_type Command type is changing
* @param extra_col_delay Any extra delay due to a read/write switch
* @return true if a packet is scheduled to a rank which is available else
* false
*/
bool chooseNext(std::deque<DRAMPacket*>& queue, bool switched_cmd_type);
bool chooseNext(std::deque<DRAMPacket*>& queue, Tick extra_col_delay);
/**
* For FR-FCFS policy reorder the read/write queue depending on row buffer
* hits and earliest banks available in DRAM
* Prioritizes accesses to the same rank as previous burst unless
* controller is switching command type.
* hits and earliest bursts available in DRAM
*
* @param queue Queued requests to consider
* @param switched_cmd_type Command type is changing
* @param extra_col_delay Any extra delay due to a read/write switch
* @return true if a packet is scheduled to a rank which is available else
* false
*/
bool reorderQueue(std::deque<DRAMPacket*>& queue, bool switched_cmd_type);
bool reorderQueue(std::deque<DRAMPacket*>& queue, Tick extra_col_delay);
/**
* Find which are the earliest banks ready to issue an activate
@ -599,11 +597,12 @@ class DRAMCtrl : public AbstractMemory
* Also checks if the bank is already prepped.
*
* @param queue Queued requests to consider
* @param switched_cmd_type Command type is changing
* @param time of seamless burst command
* @return One-hot encoded mask of bank indices
* @return boolean indicating burst can issue seamlessly, with no gaps
*/
uint64_t minBankPrep(const std::deque<DRAMPacket*>& queue,
bool switched_cmd_type) const;
std::pair<uint64_t, bool> minBankPrep(const std::deque<DRAMPacket*>& queue,
Tick min_col_at) const;
/**
* Keep track of when row activations happen, in order to enforce