cachelab finished again
This commit is contained in:
BIN
cachelab/Cache
BIN
cachelab/Cache
Binary file not shown.
1930
cachelab/Cache.bak
1930
cachelab/Cache.bak
File diff suppressed because it is too large
Load Diff
@ -1,403 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//// Copyright 2022 by mars. //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#define GET_POWER_OF_2(X) (X == 0x00 ? 0 : \
|
||||
X == 0x01 ? 0 : \
|
||||
X == 0x02 ? 1 : \
|
||||
X == 0x04 ? 2 : \
|
||||
X == 0x08 ? 3 : \
|
||||
X == 0x10 ? 4 : \
|
||||
X == 0x20 ? 5 : \
|
||||
X == 0x40 ? 6 : \
|
||||
X == 0x80 ? 7 : \
|
||||
X == 0x100 ? 8 : \
|
||||
X == 0x200 ? 9 : \
|
||||
X == 0x400 ? 10 : \
|
||||
X == 0x800 ? 11 : \
|
||||
X == 0x1000 ? 12 : \
|
||||
X == 0x2000 ? 13 : \
|
||||
X == 0x4000 ? 14 : \
|
||||
X == 0x8000 ? 15 : \
|
||||
X == 0x10000 ? 16 : \
|
||||
X == 0x20000 ? 17 : \
|
||||
X == 0x40000 ? 18 : \
|
||||
X == 0x80000 ? 19 : \
|
||||
X == 0x100000 ? 20 : \
|
||||
X == 0x200000 ? 21 : \
|
||||
X == 0x400000 ? 22 : \
|
||||
X == 0x800000 ? 23 : \
|
||||
X == 0x1000000 ? 24 : \
|
||||
X == 0x2000000 ? 25 : \
|
||||
X == 0x4000000 ? 26 : \
|
||||
X == 0x8000000 ? 27 : \
|
||||
X == 0x10000000 ? 28 : \
|
||||
X == 0x20000000 ? 29 : \
|
||||
X == 0x40000000 ? 30 : \
|
||||
X == 0x80000000 ? 31 : \
|
||||
X == 0x100000000 ? 32 : 0)
|
||||
|
||||
/*
|
||||
全相联Data Cache,16KB大小
|
||||
每行存放64个字节
|
||||
*/
|
||||
#define DCACHE_SIZE 16384
|
||||
#define DCACHE_DATA_PER_LINE 128 // 必须是8字节的倍数
|
||||
#define DCACHE_DATA_PER_LINE_ADDR_BITS GET_POWER_OF_2(DCACHE_DATA_PER_LINE) // 必须与上面设置一致,即64字节,需要6位地址
|
||||
#define DCACHE_LINES (DCACHE_SIZE/DCACHE_DATA_PER_LINE)
|
||||
#define MAX_AGE (DCACHE_LINES-1) // 每行有一个唯一的Age
|
||||
|
||||
// Cache行的结构,包括Valid、Age、Dirty、Tag和Data。你所有的状态信息,只能记录在Cache行中!
|
||||
struct DCACHE_LineStruct
|
||||
{
|
||||
UINT8 Valid;
|
||||
UINT16 Age;
|
||||
UINT8 Dirty;
|
||||
UINT64 Tag;
|
||||
UINT8 Data[DCACHE_DATA_PER_LINE];
|
||||
} DCache[DCACHE_LINES];
|
||||
|
||||
/*
|
||||
DCache初始化代码,一般需要把DCache的有效位Valid设置为0
|
||||
模拟器启动时,会调用此InitDataCache函数
|
||||
*/
|
||||
void InitDataCache()
|
||||
{
|
||||
UINT32 i;
|
||||
printf("[%s] +-----------------------------------+\n", __func__);
|
||||
printf("[%s] | 威震天的Data Cache初始化ing.... |\n", __func__);
|
||||
printf("[%s] +-----------------------------------+\n", __func__);
|
||||
for (i = 0; i < DCACHE_LINES; i++) {
|
||||
DCache[i].Valid = 0;
|
||||
DCache[i].Age = i;
|
||||
DCache[i].Dirty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
从Memory中读入一行数据到Data Cache中
|
||||
*/
|
||||
void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 CacheLineIndex)
|
||||
{
|
||||
// 一次性从Memory中将DCACHE_DATA_PER_LINE数据读入某个Data Cache行
|
||||
// 提供了一个函数,一次可以读入8个字节
|
||||
UINT32 i;
|
||||
UINT64 ReadData;
|
||||
UINT64 AlignAddress;
|
||||
UINT64* pp;
|
||||
|
||||
AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界
|
||||
pp = (UINT64*)DCache[CacheLineIndex].Data;
|
||||
for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++)
|
||||
{
|
||||
ReadData = ReadMemory(AlignAddress + 8LL * i);
|
||||
if (DEBUG)
|
||||
printf("[%s] Address=%016llX ReadData=%016llX\n", __func__, AlignAddress + 8LL * i, ReadData);
|
||||
pp[i] = ReadData;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
将Data Cache中的一行数据,写入存储器
|
||||
*/
|
||||
void StoreDataCacheLineToMemory(UINT64 Address, UINT32 CacheLineIndex)
|
||||
{
|
||||
// 一次性将DCACHE_DATA_PER_LINE数据从某个Data Cache行写入Memory中
|
||||
// 提供了一个函数,一次可以写入8个字节
|
||||
UINT32 i;
|
||||
UINT64 WriteData;
|
||||
UINT64 AlignAddress;
|
||||
UINT64* pp;
|
||||
|
||||
AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界
|
||||
pp = (UINT64*)DCache[CacheLineIndex].Data;
|
||||
WriteData = 0;
|
||||
for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++)
|
||||
{
|
||||
WriteData = pp[i];
|
||||
WriteMemory(AlignAddress + 8LL * i, WriteData);
|
||||
if (DEBUG)
|
||||
printf("[%s] Address=%016llX WriteData=%016llX\n", __func__, AlignAddress + 8LL * i, WriteData);
|
||||
}
|
||||
}
|
||||
|
||||
// 从所有行中,找到需要替换的Cache行
|
||||
// 如果有某行的Valid=0,则返回该行行号
|
||||
// 否则,返回Age最大的行的行号
|
||||
int GetReplaceLine()
|
||||
{
|
||||
int replace_line = -1;
|
||||
UINT16 max_age = 0;
|
||||
|
||||
for (int i = 0; i < DCACHE_LINES; i++)
|
||||
{
|
||||
if (!DCache[i].Valid)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
if (DCache[i].Age > max_age)
|
||||
{
|
||||
max_age = DCache[i].Age;
|
||||
replace_line = i;
|
||||
}
|
||||
}
|
||||
return replace_line;
|
||||
}
|
||||
|
||||
// 更新Age,将HitLine指定的Cache行的Age设置为0,其他行的Age要相应调整
|
||||
// 注意!要确保所有行的Age分别为0~MAX_AGE,且唯一
|
||||
// void UpdateAge(int HitLine)
|
||||
// {
|
||||
// for (int i = 0; i < DCACHE_LINES; i++) {
|
||||
// if (i != HitLine && DCache[i].Valid && DCache[i].Age < MAX_AGE) {
|
||||
// DCache[i].Age++;
|
||||
// }
|
||||
// }
|
||||
// DCache[HitLine].Age = 0;
|
||||
// }
|
||||
|
||||
void UpdateAge(int HitLine)
|
||||
{
|
||||
UINT16 old_age = DCache[HitLine].Age;
|
||||
DCache[HitLine].Age = 0;
|
||||
|
||||
for (int i = 0; i < DCACHE_LINES; i++)
|
||||
{
|
||||
if (i != HitLine && DCache[i].Valid)
|
||||
{
|
||||
if (DCache[i].Age < old_age)
|
||||
{
|
||||
DCache[i].Age++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Data Cache访问接口,系统模拟器会调用此接口,来实现对你的Data Cache访问
|
||||
Address: 访存字节地址
|
||||
Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M')
|
||||
DataSize: 数据大小:1字节、2字节、4字节、8字节
|
||||
StoreValue: 当执行写操作的时候,需要写入的数据
|
||||
LoadResult: 当执行读操作的时候,从Cache读出的数据
|
||||
*/
|
||||
UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, UINT64 StoreValue, UINT64* LoadResult)
|
||||
{
|
||||
UINT8 BlockOffset;
|
||||
UINT64 AddressTag;
|
||||
UINT8 MissFlag = 'M';
|
||||
UINT64 ReadValue;
|
||||
int HitLine = -1;
|
||||
|
||||
*LoadResult = 0;
|
||||
|
||||
// 全相联中,Address被切分为 AddressTag 和 BlockOffset
|
||||
BlockOffset = Address % DCACHE_DATA_PER_LINE;
|
||||
AddressTag = Address >> DCACHE_DATA_PER_LINE_ADDR_BITS;
|
||||
|
||||
// 查找是否命中
|
||||
for (int i = 0; i < DCACHE_LINES; i++) {
|
||||
if (DCache[i].Valid == 1 && DCache[i].Tag == AddressTag) {
|
||||
MissFlag = 'H';
|
||||
HitLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (MissFlag == 'H') // 命中!
|
||||
{
|
||||
if (Operation == 'L') // 读操作
|
||||
{
|
||||
ReadValue = 0;
|
||||
switch (DataSize)
|
||||
{
|
||||
case 1: // 1个字节
|
||||
ReadValue = DCache[HitLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
case 2: // 2个字节
|
||||
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
|
||||
ReadValue = DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
case 4: // 4个字节
|
||||
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
|
||||
ReadValue = DCache[HitLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
case 8: // 8个字节
|
||||
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
|
||||
ReadValue = DCache[HitLine].Data[BlockOffset + 7]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 6]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 5]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 4]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[HitLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
}
|
||||
*LoadResult = ReadValue;
|
||||
if (DEBUG)
|
||||
printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX ReadValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue, ReadValue);
|
||||
}
|
||||
else if (Operation == 'S' || Operation == 'M') // 写操作(修改操作在此等价于写操作)
|
||||
{
|
||||
if (DEBUG)
|
||||
printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue);
|
||||
switch (DataSize)
|
||||
{
|
||||
case 1: // 1个字节
|
||||
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF;
|
||||
break;
|
||||
case 2: // 2个字节
|
||||
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
|
||||
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF;
|
||||
break;
|
||||
case 4: // 4个字节
|
||||
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
|
||||
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 3] = StoreValue & 0xFF;
|
||||
break;
|
||||
case 8: // 8个字节
|
||||
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
|
||||
DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[HitLine].Data[BlockOffset + 7] = StoreValue & 0xFF;
|
||||
break;
|
||||
}
|
||||
// 写回策略:数据只写入Cache,并设置脏位
|
||||
DCache[HitLine].Dirty = 1;
|
||||
}
|
||||
// 更新访问时间
|
||||
UpdateAge(HitLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DEBUG)
|
||||
printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue);
|
||||
|
||||
// 不命中, 获取要替换的行
|
||||
int ReplaceLine = GetReplaceLine();
|
||||
|
||||
// 如果要替换的行有效且脏,则需要写回到内存
|
||||
if (DCache[ReplaceLine].Valid == 1 && DCache[ReplaceLine].Dirty == 1)
|
||||
{
|
||||
UINT64 OldAddress = DCache[ReplaceLine].Tag << DCACHE_DATA_PER_LINE_ADDR_BITS;
|
||||
StoreDataCacheLineToMemory(OldAddress, ReplaceLine);
|
||||
}
|
||||
|
||||
// 需要从Memory中读入新的行
|
||||
LoadDataCacheLineFromMemory(Address, ReplaceLine);
|
||||
DCache[ReplaceLine].Valid = 1;
|
||||
DCache[ReplaceLine].Tag = AddressTag;
|
||||
DCache[ReplaceLine].Dirty = 0;
|
||||
|
||||
if (Operation == 'S' || Operation == 'M') // 写操作(修改操作在此等价于写操作)
|
||||
{
|
||||
// 写操作,需要将新的StoreValue更新到CacheLine中
|
||||
switch (DataSize)
|
||||
{
|
||||
case 1: // 1个字节
|
||||
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF;
|
||||
break;
|
||||
case 2: // 2个字节
|
||||
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
|
||||
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF;
|
||||
break;
|
||||
case 4: // 4个字节
|
||||
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
|
||||
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 3] = StoreValue & 0xFF;
|
||||
break;
|
||||
case 8: // 8个字节
|
||||
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
|
||||
DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
|
||||
DCache[ReplaceLine].Data[BlockOffset + 7] = StoreValue & 0xFF;
|
||||
break;
|
||||
}
|
||||
DCache[ReplaceLine].Dirty = 1;
|
||||
}
|
||||
// 更新访问时间
|
||||
UpdateAge(ReplaceLine);
|
||||
|
||||
if (Operation == 'L') // 读操作需要返回读取的值
|
||||
{
|
||||
ReadValue = 0;
|
||||
switch (DataSize)
|
||||
{
|
||||
case 1: // 1个字节
|
||||
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
case 2: // 2个字节
|
||||
BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界
|
||||
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
case 4: // 4个字节
|
||||
BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界
|
||||
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
case 8: // 8个字节
|
||||
BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界
|
||||
ReadValue = DCache[ReplaceLine].Data[BlockOffset + 7]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 6]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 5]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 4]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
|
||||
ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0];
|
||||
break;
|
||||
}
|
||||
*LoadResult = ReadValue;
|
||||
}
|
||||
}
|
||||
return MissFlag;
|
||||
}
|
||||
|
||||
/* 指令Cache实现部分,可选实现 */
|
||||
void InitInstCache(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void LoadInstCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, UINT64* InstResult)
|
||||
{
|
||||
// 返回值'M' = Miss,'H'=Hit
|
||||
return 'M';
|
||||
}
|
||||
|
||||
1536
cachelab/Cache.c
1536
cachelab/Cache.c
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,8 @@ LDFLAGS +=
|
||||
|
||||
LDLIBS += -lzstd
|
||||
|
||||
CPPFLAGS := -Ofast -Wall -Wextra -Winline -Winit-self -Wno-sequence-point\
|
||||
|
||||
CPPFLAGS := -O3 -Wall -Wextra -Winline -Winit-self -Wno-sequence-point\
|
||||
-Wno-unused-function -Wno-inline -fPIC -W -Wcast-qual -Wpointer-arith -Icbsl/include
|
||||
|
||||
#CPPFLAGS := -g
|
||||
@ -19,7 +20,7 @@ objects = Cache.o CacheHelper.o getopt.o cbsl/src/buffer.o cbsl/src/file.o cbsl/
|
||||
all: $(PROGRAMS)
|
||||
|
||||
Cache : $(objects)
|
||||
icx $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
gcc $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||
rm -f $(objects)
|
||||
|
||||
clean:
|
||||
|
||||
Reference in New Issue
Block a user