Мне случалось использовать Simplescalar раньше.На самом деле Simplescalar уже реализовал настоящий алгоритм LRU.
Следующий комментарий четко описывает функцию update_way_list.
/* insert BLK into the order way chain in SET at location WHERE */
static void
update_way_list(struct cache_set_t *set, /* set contained way chain */
struct cache_blk_t *blk, /* block to insert */
enum list_loc_t where) /* insert location */
Код, который вы цитировали, взят из случая "пропуска кэша", когдаДоступ к кешу:
switch (cp->policy) {
case LRU:
case FIFO:
repl = cp->sets[set].way_tail;
update_way_list(&cp->sets[set], repl, Head);
break;
}
Здесь последний путь набора выбран в качестве жертвы, и он перемещен в Главу набора.Позже данные замененного блока записываются обратно, а затем жертва заменяется новым блоком данных.
Самая важная часть, которая различает LRU и FIFO, происходит из случая "попадания в кэш":
/* if LRU replacement and this is not the first element of list, reorder */
if (blk->way_prev && cp->policy == LRU)
{
/* move this block to head of the way (MRU) list */
update_way_list(&cp->sets[set], blk, Head);
}
Следовательно, пути в наборе следуют в порядке убывания возраста: Голова набора - это блок MRU («Последние использованные»), а хвост - это LRU.
Это настоящий алгоритм LRU: при попадании в кэш блок попаданий переводится в режим MRU, сохраняя при этом порядок других.Когда происходит потеря кэша, блок LRU выбирается в качестве жертвы, а новый блок размещается способом MRU.Если мы удалим предыдущий код «попадания в кэш», история доступа не будет записана, и пути в наборе будут следовать порядку доступа, таким образом обеспечивая поведение FIFO.Если мы уберем строку
update_way_list(&cp->sets[set], repl, Head);
в предыдущем коде «пропуска кэша», то новый блок будет размещен в LRU, что обеспечит поведение LIP (LRU Insertion Policy).