Реализация кэш-компрессии по алгоритму base+delta

Введение

Существенную часть кристалла современных ЦП занимает кэш-память. Дальнейшее увеличение кэш-памяти без изменения технологических норм приведет к соответствующему увеличению кристалла. Одним из способов увеличения объема хранимой информации в кэше без увеличения объёма самого кэша является использование алгоритмов компрессии. Среди них выделяются алгоритмы Base+Delta и Base-Delta-Immediate. Данная статья посвящена реализации первого алгоритма

Описание Base+Delta 

Кэш-память состоит из кэш-строк фиксированного объема. 

Кэш-строку разделяют на несколько сегментов. Нулевой сегмент называют базой. При равенстве старших бит сегментов между собой возможно сжатие с помощью разности сегмента и базы, что и называется дельтой. В зависимости от размеров базы и дельт разделяют несколько режимов

B8-∆1 (Base = 8 Byte, ∆ = 1 Byte)
B8-∆2
B8-∆4
B4-∆1
B4-∆2
B2-∆1

Ниже представлены примеры компрессии 32-ух байтовых кэш-строк

3d2e9b19c1c075d4f21fdda989e19668.png

Декомпрессия происходит с помощью суммы базы и дельт. В данном примере осуществляется компрессия в режиме B4-∆1. Исходная строка, равная 32-ум байтам, сжимается до 12-ти байт

Данный алгоритм компрессии обоснован, во-первых, регулярностью расположения данных в памяти и, во-вторых, малым динамическим диапазоном значений в кэш-памяти. Объем сегментов отличается в зависимости от типа сжимаемых данных. Он может быть равен 2, 4 и 8 байтам. Использование только одного фиксированного объема сегментов может существенно повлиять на степень компрессии в худшую сторону. Например, для массива указателей объемом 4 байта оптимально использовать 4-ех байтовые сегменты, а для массива 2-ух байтовых целых чисел — сегменты объемом по 2 байта

Значением базы всегда выбирается нулевой сегмент, так как поиск минимального значения базы потребует сложной логики, что существенно скажется на латентности компресси. Более того, по заявлениям разработчиков алгоритма степень компрессии не увеличится значительно, если реализовать логику поиска базы

Кэш-память

Кэш-память имеет 16 кэш-строк объемом 64 байта, что позволяет хранить 1МБ данных без компрессии. У каждой кэш-строки помимо данных имеется бит достоверности, определяющий актуальность и достоверность данных, 8-ми байтовое поле тега, хранящий адрес младшего слова в памяти, 8 невидимых байт, о предназначении которых будет упомянуто в последующих разделах, и 3 младших бита, в которых хранится код компрессии

Каждая кэш-строка имеет вход, куда подаются данные, которые можно записать, если активен управляющий сигнал WE (Write Enable). Имеется асинхронный сброс, контролируемый сигналом reset. При сбросе все биты строки заполняются нулями. Выходные данные проходят через буферы трех состояний, контролируемые сигналом OE (Output Enable). На выходе появляются данные только при активности данного сигнала. Описание кэш-строки представлено ниже

module cacheline(input tri [643:0] d, input tri clk, reset, we, oe, output tri [643:0] q);
	logic [643:0] d1;
	always_ff @(posedge clk, posedge reset)
	begin
		if (reset) d1 <= 644'b0;
		else if (we) d1 <= d;
		else d1 <= d1;
	end
	assign q = oe ? d1 : 644'bz;
endmodule

Для выбора строки кэш-памяти используется 4-ех битный адрес, который внутренним декодером преобразуется в 16-ти битный one-hot код. Схема декодера описана ниже

module decoder4_16(input tri [3:0] a, output logic [15:0] y);
	always_comb
	case (a)
		4'b0000: y = 16'h0001;
		4'b0001: y = 16'h0002;
		4'b0010: y = 16'h0004;
		4'b0011: y = 16'h0008;
		4'b0100: y = 16'h0010;
		4'b0101: y = 16'h0020;
		4'b0110: y = 16'h0040;
		4'b0111: y = 16'h0080;
		4'b1000: y = 16'h0100;
		4'b1001: y = 16'h0200;
		4'b1010: y = 16'h0400;
		4'b1011: y = 16'h0800;
		4'b1100: y = 16'h1000;
		4'b1101: y = 16'h2000;
		4'b1110: y = 16'h4000;
		4'b1111: y = 16'h8000;
		default: y = 16'bz;
	endcase
endmodule

Каждая кэш-строка имеет собственные сигналы WE и OE, что позволяет работать только с одной строкой в данный момент. На вход кэш-памяти подается управляющий сигнал RD (ReaD), указывающий на осуществления операции чтения или записи. В случае его активности значению сигналов OE присваивается значение декодированного адреса, так что выходом кэш-памяти является кэш-строка с активным сигналом OE, это операция чтения. В случае, если RD неактивен, то значению сигналов OE присваиваются нули, а значению WE присваивается one-hot код, так что только одна кэш-строка захватывает данные на входе кэш-памяти, это операция записи. Кэш-память также имеет сигнал асинхронного сброса, который является единым для всех кэш-строк, позволяющий сбросить каждую строку. Схема кэш-памяти представлена ниже

module cache(input tri [643:0] d, input tri [3:0] addr, input tri clk, reset, rd, output tri [643:0] q);
	tri [15:0] localaddr;
	tri [15:0] we, oe;
	decoder4_16 decoder(.a(addr), .y(localaddr));
	assign we = rd ? 16'b0 : localaddr;
	assign oe = rd ? localaddr : 16'b0;
	cacheline w0(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[0]), .oe(oe[0]), .q(q[643:0]));    
	cacheline w1(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[1]), .oe(oe[1]), .q(q[643:0]));	
	cacheline w2(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[2]), .oe(oe[2]), .q(q[643:0]));    
	cacheline w3(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[3]), .oe(oe[3]), .q(q[643:0]));
	cacheline w4(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[4]), .oe(oe[4]), .q(q[643:0]));    
	cacheline w5(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[5]), .oe(oe[5]), .q(q[643:0]));	
	cacheline w6(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[6]), .oe(oe[6]), .q(q[643:0]));    
	cacheline w7(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[7]), .oe(oe[7]), .q(q[643:0]));
	cacheline w8(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[8]), .oe(oe[8]), .q(q[643:0]));    
	cacheline w9(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[9]), .oe(oe[9]), .q(q[643:0]));	
	cacheline w10(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[10]), .oe(oe[10]), .q(q[643:0]));    
	cacheline w11(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[11]), .oe(oe[11]), .q(q[643:0]));
	cacheline w12(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[12]), .oe(oe[12]), .q(q[643:0]));    
	cacheline w13(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[13]), .oe(oe[13]), .q(q[643:0]));	
	cacheline w14(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[14]), .oe(oe[14]), .q(q[643:0]));    
	cacheline w15(.d(d[643:0]), .clk(clk), .reset(reset), .we(we[15]), .oe(oe[15]), .q(q[643:0]));
endmodule

Бит достоверности может задавать только компрессор при передаче данных и сигнал reset. Отсутствие какой-либо кэш-когерентности связано с отсутствием необходимости при работе над кэш-компрессией

Данный кэш имеет алгоритм прямого отображения. Это связано не с эффективностью, а с удобством реализации, так как для реализации кэш-компрессии не важен алгоритм ассоциативности. Для обращения в кэш следует проверить бит достоверности и сравнить поле тега с полем запроса только в одной кэш-строке, что очень удобно

Мою реализацию отличает то, что кэш-память имеет традиционный вид без использования двух полей тегов, хотя и имеет небольшие модификации в виде хранении кода компресси (их можно хранить и в отдельной таблице, если нужно) и невидимых байт.

Метод увеличения хранимой информации

Одной из проблем в реализации кэш-компрессии стоит увеличение хранимой информации. Сжимая кэш-строку, мы не можем записать что-либо в свободное пространство. В связи с этим было придумано решение в виде сжатия данных из внешнего источника и записи сжатых данных в кэш-память. Используется специальный буфер чтения, который имеет вдвое больший объём, чем кэш-строка — 128 байт. Значение буфер чтения сжимается по одному из алгоритмов компрессии и записывается в кэш-память

Исходный объём

Режим компрессии

Сжатый объём

Количество сегментов

128 Byte

B8-∆1

24 Byte

16

128 Byte

B8-∆2

40 Byte

16

128 Byte

B8-∆4

72 Byte

16

128 Byte

B4-∆1

36 Byte

32

128 Byte

B4-∆2

68 Byte

32

128 Byte

B2-∆1

66 Byte

64

Максимальный объемом сжатых данных равен 72-ум байтам. В связи с этим были добавлены 8 невидимых байт в каждую кэш-строку, которые будут нужны только при использовании алгоритмов, превышающие порог в 64 байта

Наша система оперирует 64 битными словами, поэтому кэш-строка без компрессии имеет 8 слов. Но с компрессией кэш-строка может хранить в два раза больше слов — 16. Причем режим компрессии не играет роли

Особым случаем является невозможность компрессии. Тогда можно было бы записать данные буфера в две кэш-строки, но это может привести к уничтожению сжатых данных в соседней кэш-строке, поэтому разумнее отбросить некоторые слова буфера чтения и записать их в кэш-память. Если степень компрессии в данном приложении будет минимальной, если это может отрицательно повлиять на производительность, можно отключить компрессию с помощью сигнала CON (Compression On). В таком режиме кэш-память ведет себе как обычная.

Компрессор

На вход компрессора подаются данные из основной оперативной памяти. Эти данные записываются в буфер чтения. Буферов чтения может быть несколько, также может быть несколько компрессоров, если реализовать порядок последовательной записи из компрессоров. Он также имеет асинхронный сбор

Биты буфера чтения проходят через компараторы, которые определяют возможность компрессии по одному из режимов. Одни компараторы сравнивают между собой младшие биты сегментов. Важно, чтобы значение младших бит базы было меньше или равно значениям младших бит сегментов. Достаточно трех таких компараторов для режимов B8-∆4, B4-∆2 и B2-∆1. Чтобы понять, что это так, рассмотрим следующий пример. Предположим, что схема может быть сжата по алгоритму B8-∆1. Тогда значение компараторов для B8-∆4 будет полностью соответствовать значению компараторов для B8-∆1, так как на результат будет влиять только один младший байт, ведь остальные 3 байта равны

assign cmpr[0] = ((q[31:0] >= q[991:960]) & (q[95:64] >= q[991:960]) & (q[159:128] >= q[991:960]) & (q[223:192] >= q[991:960]) & (q[287:256] >= q[991:960]) & (q[351:320] >= q[991:960]) & (q[415:384] >= q[991:960]) & (q[479:448] >= q[991:960]) & (q[543:512] >= q[991:960]) & (q[607:576] >= q[991:960]) & (q[671:640] >= q[991:960]) & (q[735:704] >= q[991:960]) & (q[799:768] >= q[991:960]) & (q[863:832] >= q[991:960]) & (q[927:896] >= q[991:960]));
assign cmpr[1] = ((q[15:0] >= q[1007:992]) & (q[47:32] >= q[1007:992]) & (q[79:64] >= q[1007:992]) & (q[111:96] >= q[1007:992]) & (q[143:128] >= q[1007:992]) & (q[175:160] >= q[1007:992]) & (q[207:192] >= q[1007:992]) & (q[239:224] >= q[1007:992]) & (q[271:256] >= q[1007:992]) & (q[303:288] >= q[1007:992]) & (q[335:320] >= q[1007:992]) & (q[367:352] >= q[1007:992]) & (q[399:384] >= q[1007:992]) & (q[431:416] >= q[1007:992]) & (q[463:448]>= q[1007:992]) & (q[495:480] >= q[1007:992]) & (q[527:512] >= q[1007:992]) & (q[559:544] >= q[1007:992]) & (q[591:576] >= q[1007:992]) & (q[623:608] >= q[1007:992]) & (q[655:640] >= q[1007:992]) & (q[687:672] >= q[1007:992]) & (q[719:704] >= q[1007:992]) &(q[751:736] >= q[1007:992]) & (q[783:768] >= q[1007:992]) & (q[815:800] >= q[1007:992]) & (q[847:832] >= q[1007:992]) & (q[879:864] >= q[1007:992]) & (q[911:896] >= q[1007:992]) & (q[943:928] >= q[1007:992]) & (q[975:960] >= q[1007:992]));
assign cmpr[2] = ((q[7:0] >= q[1015:1008]) & (q[23:16] >= q[1015:1008]) & (q[39:32] >= q[1015:1008]) & (q[55:48] >= q[1015:1008]) & (q[71:64] >= q[1015:1008]) &(q[87:80] >= q[1015:1008]) & (q[103:96] >= q[1015:1008]) & (q[119:112] >= q[1015:1008]) & (q[135:128] >= q[1015:1008]) & (q[151:144] >= q[1015:1008]) & (q[167:160] >= q[1015:1008]) & (q[183:176] >= q[1015:1008]) & (q[199:192] >= q[1015:1008]) & (q[215:208] >= q[1015:1008]) & (q[231:224] >= q[1015:1008]) & (q[247:240] >= q[1015:1008]) & (q[263:256] >= q[1015:1008]) & (q[279:272] >= q[1015:1008]) &(q[295:288] >= q[1015:1008]) & (q[311:304] >= q[1015:1008]) & (q[327:320] >= q[1015:1008]) & (q[343:336] >= q[1015:1008]) & (q[359:352] >= q[1015:1008]) & (q[375:368] >= q[1015:1008]) & (q[391:384] >= q[1015:1008]) & (q[407:400] >= q[1015:1008]) & (q[423:416] >= q[1015:1008]) & (q[439:432] >= q[1015:1008]) & (q[455:448] >= q[1015:1008]) & (q[471:464] >= q[1015:1008]) & (q[487:480] >= q[1015:1008]) & (q[503:496] >= q[1015:1008]) & (q[519:512] >= q[1015:1008]) & (q[535:528] >= q[1015:1008]) & (q[551:544] >= q[1015:1008]) & (q[567:560] >= q[1015:1008]) & (q[583:576] >= q[1015:1008]) & (q[599:592] >= q[1015:1008]) & (q[615:608] >= q[1015:1008]) & (q[631:624] >= q[1015:1008]) & (q[647:640] >= q[1015:1008]) & (q[663:656] >= q[1015:1008]) & (q[679:672] >= q[1015:1008]) & (q[695:688] >= q[1015:1008]) & (q[711:704] >= q[1015:1008]) & (q[727:720] >= q[1015:1008]) & (q[743:736]>= q[1015:1008]) & (q[759:752] >= q[1015:1008]) & (q[775:768] >= q[1015:1008]) & (q[791:784] >= q[1015:1008]) & (q[807:800] >= q[1015:1008]) & (q[823:816] >= q[1015:1008]) & (q[839:832] >= q[1015:1008]) & (q[855:848] >= q[1015:1008]) & (q[871:864] >= q[1015:1008]) & (q[887:880] >= q[1015:1008]) & (q[903:896] >= q[1015:1008]) & (q[919:912] >= q[1015:1008]) & (q[935:928] >= q[1015:1008]) & (q[951:944] >= q[1015:1008]) & (q[967:960] >= q[1015:1008]) & (q[983:976] >= q[1015:1008]) & (q[999:992] >= q[1015:1008]));

Другие компараторы сравнивают старшие разряды сегментов на равенство. Если старшие разряды сегментов равны, а младшие разряды сегментов больше, либо равны младшим разрядам базы, то возможна компрессия по этому режиму. Так формируется значение сигналов bd

assign bd[0] = ((q[63:8] == q[127:72]) & (q[127:72] == q[191:136]) & (q[191:136]== q[255:200]) & (q[388:328] == q[447:392]) & (q[447:392] == q[511:456]) & (q[511:456] == q[575:520]) & (q[575:520] == q[639:584]) & (q[639:584] == q[703:648]) & (q[703:648] == q[767:712]) & (q[767:712] == q[831:776]) & (q[831:776] == q[895:840]) & (q[895:840] == q[959:904]) & (q[959:904] == q[1023:968])) & cmpr[0];
assign bd[1] = ((q[63:16] == q[127:80]) & (q[127:80] == q[191:144]) & (q[191:144] == q[255:208]) & (q[255:208] == q[319:272]) & (q[319:272] == q[383:336]) & (q[383:336] == q[447:400]) & (q[447:400] == q[511:464]) & (q[511:464] == q[575:528])  & (q[575:528] == q[639:592]) & (q[639:592] == q[703:656]) & (q[703:656] == q[767:720]) & (q[767:720] == q[831:784]) & (q[831:784] == q[895:848]) & (q[895:848] == q[959:912]) & (q[959:912] == q[1023:976])) & cmpr[0];
assign bd[2] = ((q[63:32] == q[127:96]) & (q[127:96] == q[191:160]) & (q[191:160] == q[255:224]) & (q[255:224] == q[319:288]) & (q[319:288] == q[383:352]) & (q[383:352] == q[447:416]) & (q[447:416] == q[511:480]) & (q[511:480] == q[575:544])  & (q[575:544] == q[639:608]) & (q[639:608] == q[703:672]) & (q[703:672] == q[767:736]) & (q[767:736] == q[831:800]) & (q[831:800] == q[895:864]) & (q[895:864] == q[959:928]) & (q[959:928] == q[1023:992])) & cmpr[0]; 
assign bd[3] = ((q[31:8] == q[63:40]) & (q[63:40] == q[95:72]) & (q[95:72] == q[127:104]) & (q[127:104] == q[159:136]) & (q[159:136] == q[191:168]) & (q[191:168] == q[223:200]) & (q[223:200] == q[255:232]) & (q[255:232] == q[287:264]) & (q[287:264] == q[319:296]) & (q[319:296] == q[351:328]) & (q[351:328] == q[383:360])  & (q[383:360] == q[415:392]) & (q[415:392] == q[447:424]) & (q[447:424] == q[479:456]) & (q[479:456] == q[511:488]) & (q[511:488] == q[543:520]) & (q[543:520] == q[575:552]) & (q[575:552] == q[607:584]) & (q[607:584] == q[639:616]) & (q[639:616] == q[671:648]) & (q[671:648] == q[703:680]) & (q[703:680] == q[735:712]) & (q[735:712] == q[767:744]) & (q[767:744] == q[799:776]) & (q[799:776] == q[831:808]) & (q[831:808] == q[863:840]) & (q[863:840] == q[895:872]) & (q[895:872] == q[927:904]) & (q[927:904] == q[959:936]) & (q[959:936] == q[991:968]) & (q[991:968] == q[1023:1000])) & cmpr[1];
assign bd[4] = ((q[31:16] == q[63:48]) & (q[63:48] == q[95:80]) & (q[95:80] == q[127:112]) & (q[127:112] == q[159:144]) & (q[159:144] == q[191:176]) & (q[191:176] == q[223:208]) & (q[223:208] == q[255:240]) & (q[255:240] == q[287:272]) & (q[287:272] == q[319:304]) & (q[319:304] == q[351:336]) & (q[351:336] == q[383:368]) & (q[383:368] == q[415:400]) & (q[415:400] == q[447:432]) & (q[447:432] == q[479:464]) & (q[479:464] == q[511:496]) & (q[511:496] == q[543:528]) & (q[543:528] == q[575:560]) & (q[575:560] == q[607:592]) & (q[607:592] == q[639:624]) & (q[639:624] == q[671:656]) & (q[671:656] == q[703:688]) & (q[703:688] == q[735:720]) & (q[735:720] == q[767:752]) & (q[767:752] == q[799:784]) & (q[799:784] == q[831:816]) & (q[831:816] == q[863:848]) & (q[863:848] == q[895:880]) & (q[895:880]== q[927:912]) & (q[927:912] == q[959:944]) & (q[959:944] == q[991:976]) & (q[991:976] == q[1023:1008])) & cmpr[1];
assign bd[5] = ((q[15:8] == q[31:24]) & (q[31:24] == q[47:40]) & (q[47:40] == q[63:56]) & (q[63:56] == q[79:72]) & (q[79:72] == q[95:88]) & (q[95:88] == q[111:104]) & (q[111:104] == q[127:120]) & (q[127:120] == q[143:136]) & (q[143:136] == q[159:152]) & (q[159:152] == q[175:168]) & (q[175:168] == q[191:184]) & (q[191:184] == q[207:200]) & (q[207:200] == q[223:216]) & (q[223:216] == q[239:232]) & (q[239:232] == q[255:248]) & (q[255:248] == q[271:264]) & (q[271:264] == q[287:280]) & (q[287:280] == q[303:296]) & (q[303:296] == q[319:312]) & (q[319:312] == q[335:328]) & (q[335:328] == q[351:344]) & (q[351:344] == q[367:360]) & (q[367:360] == q[383:376]) & (q[383:376] == q[399:392]) & (q[399:392] == q[415:408]) & (q[415:408] == q[431:424]) & (q[431:424] == q[447:440]) & (q[447:440] == q[463:456]) & (q[463:456] == q[479:472]) & (q[479:472] == q[495:488]) & (q[495:488] == q[511:504]) & (q[511:504] == q[527:520]) & (q[527:520] == q[543:536]) & (q[543:536] == q[559:552]) & (q[559:552] == q[575:568]) & (q[575:568] == q[591:584]) & (q[591:584] == q[607:600]) & (q[607:600] == q[623:616]) & (q[623:616] == q[639:632]) & (q[639:632] == q[655:648]) & (q[655:648] == q[671:664]) & (q[671:664] == q[687:680]) & (q[687:680] == q[703:696]) & (q[703:696] == q[719:712]) & (q[719:712]== q[735:728]) & (q[735:728] == q[751:744]) & (q[751:744] == q[767:760]) & (q[767:760] == q[783:776]) & (q[783:776] == q[799:792]) & (q[799:792] == q[815:808])& (q[815:808] == q[831:824]) & (q[831:824] == q[847:840]) & (q[847:840] == q[863:856]) & (q[863:856] == q[879:872]) & (q[879:872] == q[895:888]) & (q[895:888] == q[911:904]) & (q[911:904] == q[927:920]) & (q[927:920] == q[943:936]) & (q[943:936] == q[959:952]) & (q[959:952] == q[975:968]) & (q[975:968] == q[991:984]) & (q[991:984] == q[1007:1000]) & (q[1007:1000] == q[1023:1016])) & cmpr[2];
assign bd[6] = ~(bd[0] | bd[1] | bd[2] | bd[3] | bd[4] | bd[5]); 

Сигналы bd подаются на схему приоритета, чтобы не возникал конфликт в случае, когда компрессия может быть осуществлена разными способами. Между собой режимы не имеют приоритетов друг над другом, но они все приоритетнее отсутствия компрессии. Старший бит означает отсутствие компрессии, поэтому приоритет расставлен от младшего разряда к старшему. Ниже представлена схема приоритета.

module priority_select(input tri [6:0] a, output logic [6:0] y);
	always_comb
	casez(a)
		7'b1000000: y = 7'b1000000;
		7'b?100000: y = 7'b0100000;
		7'b??10000: y = 7'b0010000;
		7'b???1000: y = 7'b0001000;
		7'b????100: y = 7'b0000100;
		7'b?????10: y = 7'b0000010;
		7'b??????1: y = 7'b0000001;
		default: y = 7'b0000000;
	endcase
endmodule

Данное значение используется при компрессии. В зависимости от установленного режима компрессии вычисляется значение выхода компрессора. В случае удачной компрессии в кэш-строку записываются следующие данные: бит достоверности (равный 1), поле тега (копируется с буфера чтения), сжатые данные, пустое пространство (если есть) и код компрессии

Поле кода компрессии имеет три бита и вычисляется с помощью схемы, обратной схемы приоритета, представленной ниже

module getcode(input tri [6:0] a, input tri con, output logic [2:0] code);
	always_comb
	begin
		if (con)
			begin
				case(a)
				7'b0000001: code = 3'b000; //b8-1
				7'b0000010: code = 3'b001; //b8-2
				7'b0000100: code = 3'b010; //b8-4
				7'b0001000: code = 3'b011; //b4-1
				7'b0010000: code = 3'b100; //b4-2
				7'b0100000: code = 3'b101; //b2-1
				7'b1000000: code = 3'b110; //No Compression
				default: code = 3'bxxx;
			endcase
			end
		else
			begin
				code = 3'b110;
			end
	end
endmodule

Если компрессия неосуществима, то в кэш-строку записываются старшие 8 слов буфера чтения.

Если сигнал CON равен 0, что свидетельствует об отключенной компрессии, запись в кэш-строку происходит в два цикла. Когда значение T-триггера равно 1, записываются старшие слова буфера чтения. Когда значение T-триггера равно 0, записываются младшие слова буфера чтения по адресу, меньший на 1.

Т-триггер представляет из себя схему, которая за каждый такт инвертирует и выводит внутренний сигнал. При асинхронном сбросе значение внутреннего сигнала становится равным 0. Её описание представлено ниже

module ttongle(input tri clk, reset, con, output logic t);
	logic a;
	always_ff@(posedge clk, posedge reset)
	begin
		if (reset) a <= 1'b0;
		else if(~con) 
			begin
				a <= ~a;
				t <= a;
			end
		else t <= 1'bz;
	end
endmodule

Вся схема компрессора выглядит следующим образом

module compressor(input tri [1087:0] d, input tri con, clk, reset, output logic [643:0] cq, output logic [3:0] way);
	tri [6:0] bd, y;
	tri [2:0] code, cmpr;
	tri [1087:0] q;
	tri t;
	bufferc bufferc(.d(d), .clk(clk), .reset(reset), .q(q));
	assign cmpr[0] = ((q[31:0] >= q[991:960]) & (q[95:64] >= q[991:960]) & (q[159:128] >= q[991:960]) & (q[223:192] >= q[991:960]) & (q[287:256] >= q[991:960]) & (q[351:320] >= q[991:960]) & (q[415:384] >= q[991:960]) & (q[479:448] >= q[991:960]) & (q[543:512] >= q[991:960]) & (q[607:576] >= q[991:960]) & (q[671:640] >= q[991:960]) & (q[735:704] >= q[991:960]) & (q[799:768] >= q[991:960]) & (q[863:832] >= q[991:960]) & (q[927:896] >= q[991:960]));
	assign cmpr[1] = ((q[15:0] >= q[1007:992]) & (q[47:32] >= q[1007:992]) & (q[79:64] >= q[1007:992]) & (q[111:96] >= q[1007:992]) & (q[143:128] >= q[1007:992]) & (q[175:160] >= q[1007:992]) & (q[207:192] >= q[1007:992]) & (q[239:224] >= q[1007:992]) & (q[271:256] >= q[1007:992]) & (q[303:288] >= q[1007:992]) & (q[335:320] >= q[1007:992]) & (q[367:352] >= q[1007:992]) & (q[399:384] >= q[1007:992]) & (q[431:416] >= q[1007:992]) & (q[463:448]>= q[1007:992]) & (q[495:480] >= q[1007:992]) & (q[527:512] >= q[1007:992]) & (q[559:544] >= q[1007:992]) & (q[591:576] >= q[1007:992]) & (q[623:608] >= q[1007:992]) & (q[655:640] >= q[1007:992]) & (q[687:672] >= q[1007:992]) & (q[719:704] >= q[1007:992]) &(q[751:736] >= q[1007:992]) & (q[783:768] >= q[1007:992]) & (q[815:800] >= q[1007:992]) & (q[847:832] >= q[1007:992]) & (q[879:864] >= q[1007:992]) & (q[911:896] >= q[1007:992]) & (q[943:928] >= q[1007:992]) & (q[975:960] >= q[1007:992]));
	assign cmpr[2] = ((q[7:0] >= q[1015:1008]) & (q[23:16] >= q[1015:1008]) & (q[39:32] >= q[1015:1008]) & (q[55:48] >= q[1015:1008]) & (q[71:64] >= q[1015:1008]) &(q[87:80] >= q[1015:1008]) & (q[103:96] >= q[1015:1008]) & (q[119:112] >= q[1015:1008]) & (q[135:128] >= q[1015:1008]) & (q[151:144] >= q[1015:1008]) & (q[167:160] >= q[1015:1008]) & (q[183:176] >= q[1015:1008]) & (q[199:192] >= q[1015:1008]) & (q[215:208] >= q[1015:1008]) & (q[231:224] >= q[1015:1008]) & (q[247:240] >= q[1015:1008]) & (q[263:256] >= q[1015:1008]) & (q[279:272] >= q[1015:1008]) &(q[295:288] >= q[1015:1008]) & (q[311:304] >= q[1015:1008]) & (q[327:320] >= q[1015:1008]) & (q[343:336] >= q[1015:1008]) & (q[359:352] >= q[1015:1008]) & (q[375:368] >= q[1015:1008]) & (q[391:384] >= q[1015:1008]) & (q[407:400] >= q[1015:1008]) & (q[423:416] >= q[1015:1008]) & (q[439:432] >= q[1015:1008]) & (q[455:448] >= q[1015:1008]) & (q[471:464] >= q[1015:1008]) & (q[487:480] >= q[1015:1008]) & (q[503:496] >= q[1015:1008]) & (q[519:512] >= q[1015:1008]) & (q[535:528] >= q[1015:1008]) & (q[551:544] >= q[1015:1008]) & (q[567:560] >= q[1015:1008]) & (q[583:576] >= q[1015:1008]) & (q[599:592] >= q[1015:1008]) & (q[615:608] >= q[1015:1008]) & (q[631:624] >= q[1015:1008]) & (q[647:640] >= q[1015:1008]) & (q[663:656] >= q[1015:1008]) & (q[679:672] >= q[1015:1008]) & (q[695:688] >= q[1015:1008]) & (q[711:704] >= q[1015:1008]) & (q[727:720] >= q[1015:1008]) & (q[743:736]>= q[1015:1008]) & (q[759:752] >= q[1015:1008]) & (q[775:768] >= q[1015:1008]) & (q[791:784] >= q[1015:1008]) & (q[807:800] >= q[1015:1008]) & (q[823:816] >= q[1015:1008]) & (q[839:832] >= q[1015:1008]) & (q[855:848] >= q[1015:1008]) & (q[871:864] >= q[1015:1008]) & (q[887:880] >= q[1015:1008]) & (q[903:896] >= q[1015:1008]) & (q[919:912] >= q[1015:1008]) & (q[935:928] >= q[1015:1008]) & (q[951:944] >= q[1015:1008]) & (q[967:960] >= q[1015:1008]) & (q[983:976] >= q[1015:1008]) & (q[999:992] >= q[1015:1008]));
	assign bd[0] = ((q[63:8] == q[127:72]) & (q[127:72] == q[191:136]) & (q[191:136]== q[255:200]) & (q[388:328] == q[447:392]) & (q[447:392] == q[511:456]) & (q[511:456] == q[575:520]) & (q[575:520] == q[639:584]) & (q[639:584] == q[703:648]) & (q[703:648] == q[767:712]) & (q[767:712] == q[831:776]) & (q[831:776] == q[895:840]) & (q[895:840] == q[959:904]) & (q[959:904] == q[1023:968])) & cmpr[0];
	assign bd[1] = ((q[63:16] == q[127:80]) & (q[127:80] == q[191:144]) & (q[191:144] == q[255:208]) & (q[255:208] == q[319:272]) & (q[319:272] == q[383:336]) & (q[383:336] == q[447:400]) & (q[447:400] == q[511:464]) & (q[511:464] == q[575:528])  & (q[575:528] == q[639:592]) & (q[639:592] == q[703:656]) & (q[703:656] == q[767:720]) & (q[767:720] == q[831:784]) & (q[831:784] == q[895:848]) & (q[895:848] == q[959:912]) & (q[959:912] == q[1023:976])) & cmpr[0];
	assign bd[2] = ((q[63:32] == q[127:96]) & (q[127:96] == q[191:160]) & (q[191:160] == q[255:224]) & (q[255:224] == q[319:288]) & (q[319:288] == q[383:352]) & (q[383:352] == q[447:416]) & (q[447:416] == q[511:480]) & (q[511:480] == q[575:544])  & (q[575:544] == q[639:608]) & (q[639:608] == q[703:672]) & (q[703:672] == q[767:736]) & (q[767:736] == q[831:800]) & (q[831:800] == q[895:864]) & (q[895:864] == q[959:928]) & (q[959:928] == q[1023:992])) & cmpr[0]; 
	assign bd[3] = ((q[31:8] == q[63:40]) & (q[63:40] == q[95:72]) & (q[95:72] == q[127:104]) & (q[127:104] == q[159:136]) & (q[159:136] == q[191:168]) & (q[191:168] == q[223:200]) & (q[223:200] == q[255:232]) & (q[255:232] == q[287:264]) & (q[287:264] == q[319:296]) & (q[319:296] == q[351:328]) & (q[351:328] == q[383:360])  & (q[383:360] == q[415:392]) & (q[415:392] == q[447:424]) & (q[447:424] == q[479:456]) & (q[479:456] == q[511:488]) & (q[511:488] == q[543:520]) & (q[543:520] == q[575:552]) & (q[575:552] == q[607:584]) & (q[607:584] == q[639:616]) & (q[639:616] == q[671:648]) & (q[671:648] == q[703:680]) & (q[703:680] == q[735:712]) & (q[735:712] == q[767:744]) & (q[767:744] == q[799:776]) & (q[799:776] == q[831:808]) & (q[831:808] == q[863:840]) & (q[863:840] == q[895:872]) & (q[895:872] == q[927:904]) & (q[927:904] == q[959:936]) & (q[959:936] == q[991:968]) & (q[991:968] == q[1023:1000])) & cmpr[1];
	assign bd[4] = ((q[31:16] == q[63:48]) & (q[63:48] == q[95:80]) & (q[95:80] == q[127:112]) & (q[127:112] == q[159:144]) & (q[159:144] == q[191:176]) & (q[191:176] == q[223:208]) & (q[223:208] == q[255:240]) & (q[255:240] == q[287:272]) & (q[287:272] == q[319:304]) & (q[319:304] == q[351:336]) & (q[351:336] == q[383:368]) & (q[383:368] == q[415:400]) & (q[415:400] == q[447:432]) & (q[447:432] == q[479:464]) & (q[479:464] == q[511:496]) & (q[511:496] == q[543:528]) & (q[543:528] == q[575:560]) & (q[575:560] == q[607:592]) & (q[607:592] == q[639:624]) & (q[639:624] == q[671:656]) & (q[671:656] == q[703:688]) & (q[703:688] == q[735:720]) & (q[735:720] == q[767:752]) & (q[767:752] == q[799:784]) & (q[799:784] == q[831:816]) & (q[831:816] == q[863:848]) & (q[863:848] == q[895:880]) & (q[895:880]== q[927:912]) & (q[927:912] == q[959:944]) & (q[959:944] == q[991:976]) & (q[991:976] == q[1023:1008])) & cmpr[1];
	assign bd[5] = ((q[15:8] == q[31:24]) & (q[31:24] == q[47:40]) & (q[47:40] == q[63:56]) & (q[63:56] == q[79:72]) & (q[79:72] == q[95:88]) & (q[95:88] == q[111:104]) & (q[111:104] == q[127:120]) & (q[127:120] == q[143:136]) & (q[143:136] == q[159:152]) & (q[159:152] == q[175:168]) & (q[175:168] == q[191:184]) & (q[191:184] == q[207:200]) & (q[207:200] == q[223:216]) & (q[223:216] == q[239:232]) & (q[239:232] == q[255:248]) & (q[255:248] == q[271:264]) & (q[271:264] == q[287:280]) & (q[287:280] == q[303:296]) & (q[303:296] == q[319:312]) & (q[319:312] == q[335:328]) & (q[335:328] == q[351:344]) & (q[351:344] == q[367:360]) & (q[367:360] == q[383:376]) & (q[383:376] == q[399:392]) & (q[399:392] == q[415:408]) & (q[415:408] == q[431:424]) & (q[431:424] == q[447:440]) & (q[447:440] == q[463:456]) & (q[463:456] == q[479:472]) & (q[479:472] == q[495:488]) & (q[495:488] == q[511:504]) & (q[511:504] == q[527:520]) & (q[527:520] == q[543:536]) & (q[543:536] == q[559:552]) & (q[559:552] == q[575:568]) & (q[575:568] == q[591:584]) & (q[591:584] == q[607:600]) & (q[607:600] == q[623:616]) & (q[623:616] == q[639:632]) & (q[639:632] == q[655:648]) & (q[655:648] == q[671:664]) & (q[671:664] == q[687:680]) & (q[687:680] == q[703:696]) & (q[703:696] == q[719:712]) & (q[719:712]== q[735:728]) & (q[735:728] == q[751:744]) & (q[751:744] == q[767:760]) & (q[767:760] == q[783:776]) & (q[783:776] == q[799:792]) & (q[799:792] == q[815:808])& (q[815:808] == q[831:824]) & (q[831:824] == q[847:840]) & (q[847:840] == q[863:856]) & (q[863:856] == q[879:872]) & (q[879:872] == q[895:888]) & (q[895:888] == q[911:904]) & (q[911:904] == q[927:920]) & (q[927:920] == q[943:936]) & (q[943:936] == q[959:952]) & (q[959:952] == q[975:968]) & (q[975:968] == q[991:984]) & (q[991:984] == q[1007:1000]) & (q[1007:1000] == q[1023:1016])) & cmpr[2];
	assign bd[6] = ~(bd[0] | bd[1] | bd[2] | bd[3] | bd[4] | bd[5]); 
	priority_select preget(.a(bd), .y(y));
	getcode get(.a(y), .code(code), .con(con));
	ttongle doubleclockmode(.clk(clk), .reset(reset), .con(con), .t(t));
	always_comb
	begin
		if (con)
			begin
				case (y)
					7'b0000001: cq = {1'b1, q[1087:1024], q[1023:960], 8'b0, (q[903:896] - q[967:960]), (q[839:832] - q[967:960]), (q[775:768] - q[967:960]), (q[711:704] - q[967:960]), (q[647:640] - q[967:960]), (q[583:576] - q[967:960]), (q[519:512] - q[967:960]), (q[455:448] - q[967:960]), (q[391:384] - q[967:960]), (q[327:320] - q[967:960]), (q[263:256] - q[967:960]), (q[199:192] - q[967:960]), (q[135:128] - q[967:960]), (q[71:64] - q[967:960]), (q[7:0] - q[967:960]), 384'b0, code}; //b8-1
					7'b0000010: cq = {1'b1, q[1087:1024], q[1023:960], 16'b0, (q[911:896] - q[975:960]), (q[847:832] - q[975:960]), (q[783:768] - q[975:960]), (q[719:704] - q[975:960]), (q[655:640] - q[975:960]), (q[591:576] - q[975:960]), (q[527:512] - q[975:960]), (q[463:448] - q[975:960]), (q[399:384] - q[975:960]), (q[335:320] - q[975:960]), (q[271:256] - q[975:960]), (q[207:192] - q[975:960]), (q[143:128] - q[975:960]), (q[79:64] - q[975:960]), (q[15:0] - q[975:960]), 256'b0, code}; //b8-2
					7'b0000100: cq = {1'b1, q[1087:1024], q[1023:960], 32'b0, (q[927:897] - q[991:960]), (q[863:832] - q[991:960]), (q[799:768] - q[991:960]), (q[735:704] - q[991:960]), (q[671:640] - q[991:960]), (q[607:576] - q[991:960]), (q[543:512] - q[991:960]), (q[479:448] - q[991:960]), (q[415:384] - q[991:960]), (q[351:320] - q[991:960]), (q[287:256] - q[991:960]), (q[223:192] - q[991:960]), (q[159:128] - q[991:960]), (q[95:64] - q[991:960]), (q[31:0] - q[991:960]), code}; //b8-4
					7'b0001000: cq = {1'b1, q[1087:1024], q[1023:992], 8'b0, (q[967:960] - q[999:992]), (q[935:928] - q[999:992]), (q[903:896] - q[999:992]), (q[871:864] - q[999:992]), (q[839:832] - q[999:992]), (q[807:800] - q[999:992]), (q[775:768] - q[999:992]), (q[743:736] - q[999:992]), (q[711:704] - q[999:992]), (q[679:672] - q[999:992]), (q[647:640] - q[999:992]), (q[615:608] - q[999:992]), (q[583:576] - q[999:992]), (q[551:544] - q[999:992]), (q[519:512] - q[999:992]), (q[487:480] - q[999:992]), (q[455:448] - q[999:992]), (q[423:416] - q[999:992]), (q[391:384] - q[999:992]), (q[359:352] - q[999:992]), (q[327:320] - q[999:992]), (q[295:288] - q[999:992]), (q[263:256] - q[999:992]), (q[231:224] - q[999:992]), (q[199:192] - q[999:992]), (q[167:160] - q[999:992]), (q[135:128] - q[999:992]), (q[103:96] - q[999:992]), (q[71:64] - q[999:992]), (q[39:32] - q[999:992]), (q[7:0] - q[999:992]), 288'b0, code};
					7'b0010000: cq = {1'b1, q[1087:1024], q[1023:992], 16'b0, (q[975:960] - q[1007:992]), (q[943:928] - q[1007:992]), (q[911:896] - q[1007:992]), (q[879:864] - q[1007:992]), (q[847:832] - q[1007:992]), (q[815:800] - q[1007:992]), (q[783:768] - q[1007:992]), (q[751:736] - q[1007:992]), (q[719:704] - q[1007:992]), (q[687:672] - q[1007:992]),(q[655:640] - q[1007:992]), (q[623:608] - q[1007:992]), (q[591:576] - q[1007:992]), (q[559:544] - q[1007:992]), (q[527:512] - q[1007:992]), (q[495:480] - q[1007:992]), (q[463:448] - q[1007:992]), (q[431:416] - q[1007:992]), (q[399:384] - q[1007:992]), (q[367:352] - q[1007:992]), (q[335:320] - q[1007:992]), (q[303:288] - q[1007:992]), (q[271:256] - q[1007:992]), (q[239:224] - q[1007:992]), (q[207:192] - q[1007:992]), (q[175:160] - q[1007:992]), (q[143:128] - q[1007:992]), (q[111:96] - q[1007:992]), (q[79:64] - q[1007:992]), (q[47:32] - q[1007:992]), (q[15:0] - q[1007:992]), 32'b0, code};
					7'b0100000: cq = {1'b1, q[1087:1024], q[1023:1008], 8'b0, (q[999:992] - q[1015:1008]), (q[983:978] - q[1015:1008]), (q[967:960] - q[1015:1008]), (q[951:944] - q[1015:1008]), (q[935:928] - q[1015:1008]), (q[919:912] - q[1015:1008]), (q[903:896] - q[1015:1008]), (q[887:880] - q[1015:1008]), (q[871:864] - q[1015:1008]), (q[855:848] - q[1015:1008]), (q[839:832] - q[1015:1008]), (q[823:816] - q[1015:1008]), (q[807:800] - q[1015:1008]), (q[791:784] - q[1015:1008]), (q[775:768] - q[1015:1008]), (q[759:752] - q[1015:1008]), (q[743:736] - q[1015:1008]), (q[727:720] - q[1015:1008]), (q[711:704] - q[1015:1008]), (q[695:688] - q[1015:1008]), (q[679:672] - q[1015:1008]), (q[663:656] - q[1015:1008]), (q[647:640] - q[1015:1008]), (q[631:624] - q[1015:1008]), (q[615:608] - q[1015:1008]), (q[599:592] - q[1015:1008]), (q[583:576] - q[1015:1008]), (q[567:560] - q[1015:1008]), (q[551:544] - q[1015:1008]), (q[535:528] - q[1015:1008]), (q[519:512] - q[1015:1008]), (q[503:496] - q[1015:1008]), (q[487:480] - q[1015:1008]), (q[471:464] - q[1015:1008]), (q[455:448] - q[1015:1008]), (q[439:432] - q[1015:1008]), (q[423:416] - q[1015:1008]), (q[407:400] - q[1015:1008]), (q[391:384] - q[1015:1008]), (q[375:368] - q[1015:1008]), (q[359:352] - q[1015:1008]), (q[343:336] - q[1015:1008]), (q[327:320] - q[1015:1008]), (q[311:304] - q[1015:1008]), (q[295:288] - q[1015:1008]), (q[279:272] - q[1015:1008]), (q[263:256] - q[1015:1008]), (q[247:240] - q[1015:1008]), (q[231:224] - q[1015:1008]), (q[215:208] - q[1015:1008]), (q[199:192] - q[1015:1008]), (q[183:176] - q[1015:1008]), (q[167:160] - q[1015:1008]), (q[151:144] - q[1015:1008]), (q[135:128] - q[1015:1008]), (q[119:112] - q[1015:1008]), (q[103:96] - q[1015:1008]), (q[87:80] - q[1015:1008]), (q[71:64] - q[1015:1008]), (q[55:48] - q[1015:1008]), (q[39:32] - q[1015:1008]), (q[23:16] - q[1015:1008]), (q[7:0] - q[1015:1008]), 48'b0, code};
					7'b1000000: cq = reset ? 644'b0 : {1'b1, q[1087:512], 64'b0, code};	
					default: cq = 644'bz;
				endcase
				way = q[1031:1028];
			end
		else
			begin
				if (t) 
					begin
						cq = {1'b1, q[1087:512], 64'b0, code};
						way = q[1031:1028];
					end
				else
					begin
						cq = {1'b1, q[1087:1024], q[511:0], code};
						way = q[1031:1028] - 1'b1;
					end
			end	
	end
endmodule

Транзакция с кэш-памятью

С пословной адресацией (64 бит) требуется 8 слов для заполнения кэш-строки объемом 64 байт. Отсюда следует, что 3 младших бита поля тега (адрес младшего слова в памяти) должны быть равны 0. А в теге запроса младшие три бита будут обеспечивать выбор слова в кэш-строке. Для выбора самой кэш-строки (адрес кэш-памяти) нужно считать следующие 4 бита (так как кэш-строк 16 = 2^4) тега запроса (при чтении), либо тега в буфере чтения (при записи)

Когда включена компрессия, каждая кэш-строка может содержать 8, либо 16 слов. Тогда младшие 4 бита поля тега будут равны 0. Младшие 4 бита тега запроса будут обеспечивать выбор слова из кэш-памяти. Следующие 4 бита в теге запроса или в поле тега буфера чтения будут обеспечивать выбор кэш-строки

Запись в кэш-память осуществляется только через компрессор, даже если компрессия отключена.

Операция чтения (RD активен) происходит следующим образом. По тегу запроса известно, в какой кэш-строке находятся данные. Затем поле тега передается и бит достоверности вместе с тегом запроса, а также вместе с сигналом CON передаются в фильтр запросов. Там происходит сравнение старших бит поля тега и старших бит тега запроса. Младшие 3 или 4 бита (в зависимости от сигнала CON) не сравниваются по той причине, что у тега запроса они служат для выбора слова. Если равенство устанавливается, а также бит достоверности равен 1, то сигнал кэш-попадание устанавливается в 0. В противном случае данный сигнал устанавливается в 1. 

Когда компрессия включена, но для строки нет режимов компрессии, отсеиваются запросы в первые 4 слова

Схема фильтра запросов описана ниже

module requestfilter(input tri [643:0] d, input tri [63:0] tag, input tri con, input tri [3:0] wordaddr, output logic cachemiss);
	tri filter;
	tri [63:0] tageq;
	tri [63:0] deq;
	tri code110;
	assign tageq = con ? {tag[63:4], 4'b0} : {tag[63:3], 3'b0};
	assign deq = con ? {d[642:583], 4'b0} : {d[642:582], 3'b0};
	assign code110 = con ? ((d[2:0] == 3'b110) & (wordaddr < 4'b0111)) : 1'b0;
	assign filter = code110 ? 1'b1 : 1'b0;
	always_comb
		begin
			if(d[643] & (tageq == deq)) cachemiss = 1'b0 | filter;
			else cachemiss = 1'b1;
		end
endmodule

Данные с кэш-памяти попадают в декомпрессор. Декомпрессия происходит путем сложения базы и дельт. Если компрессия отключена, либо для строки нет режима компрессии, то данные кэш-строки выводятся без декомпрессии. Декомпрессор реализован с помощью сумматоров, работающих параллельно. Декомпрессор представлен ниже

module decompressor(input tri [578:0] d, input tri clk, reset, output logic [1023:0] q);
	always_comb
	begin
		case (d[2:0]) 
			3'b000: q = {d[578:515], (d[506:499] + d[578:515]), (d[498:491] + d[578:515]), (d[490:483] + d[578:515]), (d[482:475] + d[578:515]), (d[474:467] + d[578:515]), (d[466:459] + d[578:515]), (d[458:451] + d[578:515]), (d[450:443] + d[578:515]), (d[442:435] + d[578:515]), (d[434:427] + d[578:515]), (d[426:419] + d[578:515]), (d[418:411] + d[578:515]), (d[410:403] + d[578:515]), (d[402:395] + d[578:515]), (d[394:387] + d[578:515])};
			3'b001: q = {d[578:515], (d[498:483] + d[578:515]), (d[482:467] + d[578:515]), (d[466:451] + d[578:515]), (d[450:435] + d[578:515]), (d[434:419] + d[578:515]), (d[418:403] + d[578:515]), (d[402:387] + d[578:515]), (d[386:371] + d[578:515]), (d[370:355] + d[578:515]), (d[354:339] + d[578:515]), (d[338:323] + d[578:515]), (d[322:307] + d[578:515]), (d[306:291] + d[578:515]), (d[290:275] + d[578:515]), (d[274:259] + d[578:515])};
			3'b010: q = {d[578:515], (d[482:451] + d[578:515]), (d[450:419] + d[578:515]), (d[418:387] + d[578:515]), (d[386:355] + d[578:515]), (d[354:323] + d[578:515]), (d[322:291] + d[578:515]), (d[290:259] + d[578:515]), (d[258:227] + d[578:515]), (d[226:195] + d[578:515]), (d[194:163] + d[578:515]), (d[162:131] + d[578:515]), (d[130:99] + d[578:515]), (d[98:67] + d[578:515]), (d[66:35] + d[578:515]), (d[34:3] + d[578:515])};
			3'b011: q = {d[578:547], (d[538:531] + d[578:547]), (d[530:523] + d[578:547]), (d[522:515] + d[578:547]), (d[514:507] + d[578:547]), (d[506:499] + d[578:547]), (d[498:491] + d[578:547]), (d[490:483] + d[578:547]), (d[482:475] + d[578:547]), (d[450:443] + d[578:547]), (d[442:435] + d[578:547]), (d[434:427] + d[578:547]), (d[426:419] + d[578:547]), (d[418:411] + d[578:547]), (d[410:403] + d[578:547]), (d[402:395] + d[578:547]), (d[394:387] + d[578:547]), (d[386:379] + d[578:547]), (d[378:371] + d[578:547]), (d[370:363] + d[578:547]), (d[362:355] +d[578:547]), (d[354:347] + d[578:547]), (d[346:339] + d[578:547]), (d[338:331] +d [578:547]), (d[330:323] + d[578:547]), (d[322:315] + d[578:547]), (d[314:307] + d[578:547]), (d[306:299] + d[578:547]), (d[298:291] + d[578:547])};
			3'b100: q = {d[578:547], (d[530:515] + d[578:547]), (d[514:499] + d[578:547]), (d[498:483] + d[578:547]), (d[482:467] + d[578:547]), (d[466:451] + d[578:547]), (d[450:435] + d[578:547]), (d[434:419] + d[578:547]), (d[418:403] + d[578:547]), (d[402:387] + d[578:547]), (d[386:371] + d[578:547]), (d[370:355] + d[578:547]), (d[354:339] + d[578:547]), (d[338:323] + d[578:547]), (d[322:307] + d[578:547]), (d[306:291] + d[578:547]), (d[290:275] + d[578:547]), (d[274:259] + d[578:547]), (d[258:243] + d[578:547]), (d[242:227] + d[578:547]), (d[226:211] + d[578:547]), (d[210:195] + d[578:547]), (d[194:179] + d[578:547]), (d[178:163] + d[578:547]), (d[162:147] + d[578:547]), (d[146:131] + d[578:547]), (d[130:115] + d[578:547]), (d[114:99] + d[578:547]), (d[98:83] + d[578:547]), (d[82:67] + d[578:547]), (d[66:51] + d[578:547]), (d[50:35] + d[578:547])};
			3'b101: q = {d[578:563], (d[554:547] + d[578:563]), (d[546:539] + d[578:563]), (d[538:531] + d[578:563]), (d[530:523] + d[578:563]), (d[522:515] + d[578:563]), (d[514:507] + d[578:563]), (d[506:499] + d[578:563]), (d[498:491] + d[578:563]), (d[490:483] + d[578:563]), (d[482:475] + d[578:563]), (d[474:467] + d[578:563]), (d[466:459] + d[578:563]), (d[458:451] + d[578:563]), (d[450:443] + d[578:563]), (d[442:435] + d[578:563]), (d[434:427] + d[578:563]), (d[426:419] + d[578:563]), (d[418:411] + d[578:563]), (d[410:403] + d[578:563]), (d[402:395] + d[578:563]), (d[394:387] + d[578:563]), (d[386:379] + d[578:563]), (d[378:371] +d[578:563]), (d[370:363] + d[578:563]), (d[362:355] + d[578:563]), (d[354:347] +d[578:563]), (d[346:339] + d[578:563]), (d[338:331] + d[578:563]), (d[330:323]+ d[578:563]), (d[322:315] + d[578:563]), (d[314:307] + d[578:563]), (d[306:299]+ d[578:563]), (d[298:291] + d[578:563]), (d[290:283] + d[578:563]), (d[282:275] + d[578:563]), (d[274:267] + d[578:563]), (d[266:259] + d[578:563]), (d[258:251] + d[578:563]), (d[250:243] + d[578:563]), (d[242:235] + d[578:563]), (d[234:227] + d[578:563]), (d[226:219] + d[578:563]), (d[218:211] + d[578:563]), (d[210:203] + d[578:563]), (d[202:195] + d[578:563]), (d[194:187] + d[578:563]), (d[186:179] + d[578:563]), (d[178:171] + d[578:563]), (d[170:163] + d[578:563]), (d[162:155] + d[578:563]), (d[154:147] + d[578:563]), (d[146:139] + d[578:563]), (d[138:131] + d[578:563]), (d[130:123] + d[578:563]), (d[122:115] + d[578:563]), (d[114:107] + d[578:563]), (d[106:99] + d[578:563]), (d[98:91] + d[578:563]), (d[90:83] + d[578:563]), (d[82:75] + d[578:563]), (d[74:67] + d[578:563]), (d[66:59]+ d[578:563]), (d[58:51] + d[578:563])};
			3'b110: q = {d[578:3], 448'b0};
			default: q = 1024'bz;
		endcase
	end
endmodule

Затем, имея адрес слова, схема WSU (Word Select UnIt) выводит одно из слов памяти на выход. Её описание представлено ниже

module wordselectunit(input tri [1023:0] d, input tri [3:0] addr, output logic [63:0] q);
	always_comb
	begin
		case(addr)
			4'b1111: q = d[1023:960];
			4'b1110: q = d[959:896];
			4'b1101: q = d[895:832];
			4'b1100: q = d[831:768];
			4'b1011: q = d[767:704];
			4'b1010: q = d[703:640];
			4'b1001: q = d[639:576];
			4'b1000: q = d[575:512];
			4'b0111: q = d[511:448];
			4'b0110: q = d[447:384];
			4'b0101: q = d[383:320];
			4'b0100: q = d[319:256];
			4'b0011: q = d[255:192];
			4'b0010: q = d[191:128];
			4'b0001: q = d[127:64];
			4'b0000: q = d[63:0];
			default: q = 64'bz;
		endcase
	end
endmodule

Обращение к кэш-памяти происходит стандартным образом за тем исключением, что есть схема декомпрессии. Со сжатыми кэш-строками мы работаем также, как если бы мы работали с настоящими 128-ми байтовыми кэш-строками. Данная реализация алгоритма Base+Delta не меняет принципов ассоциативности кэш-памяти.

Конечная схема

Вся система выглядит следующим образом

module cachewithcompression(input tri [1087:0] readbuffer, input tri [63:0] tag, input tri con, clk, reset, rd, output tri [63:0] q, output tri cachehit);
	tri [643:0] cq;
	tri [643:0] cqic;
	tri [1023:0] pdq, dq;
	tri [3:0] addr;
	tri cachemiss;
	tri [3:0] wordaddr;
	tri [3:0] addrread, addrwrite;
	tri [643:0] wsuq; 
	msbitoutput msbitoutput(.word(tag[3:0]), .s(con), .outputword(wordaddr));
	mux2_4 conmux(.a(tag[6:3]), .b(tag[7:4]), .s(con), .c(addrread));
	mux2_4 addrselect(.a(addrwrite), .b(addrread), .s(rd), .c(addr));
	compressor compressor(.d(readbuffer), .con(con), .clk(clk), .reset(reset), .cq(cq), .way(addrwrite));
	cache cache(.d(cq), .addr(addr), .clk(clk), .reset(reset), .rd(rd), .q(cqic));
	requestfilter requestfilter(.d(cqic), .tag(tag), .wordaddr(wordaddr), .con(con), .cachemiss(cachemiss));
	decompressor decompressor(.d(cqic), .clk(clk), .reset(reset), .q(pdq));
	bufferd bufd(.d(pdq), .clk(clk), .reset(reset), .q(dq));
	wordselectunit wsu(.d(dq), .addr(wordaddr), .q(wsuq));
	assign cachehit = ~cachemiss & rd;
	assign q = cachehit ? wsuq : 64'bz; 
endmodule

Здесь, помимо описанных модулей, есть ещё несколько мультиплексоров, обеспечивающих выбор правильного адреса для кэш-памяти. Стоит ещё отметить, что данные выводятся только при кэш-попадании. Значение кэш-попадания основано на инвертированном значении кэш-промаха и сигнала RD. То есть даже если кэш-попадание есть в принципе есть, считывание не произойдет, если используется операция записи

Полезные ссылки

В статье, как вы увидели, отсутствуют тесты. Их вы можете посмотреть у авторов-разработчиков

О таких алгоритмах я узнал от других разработчиков, статью которых прикрепляю

Залил на github

© Habrahabr.ru