龙芯GS232(MIPS 32)架构cache管理笔记和指令说明

1 mips32架构

MIPS架构是一种基于精简指令集(Reduced Instruction Set Computer,RISC)的计算机处理器架构。MIPS架构由MIPS Technologies公司在1981年开发,并在1984年发布了第一款MIPS处理器。

MIPS架构的特点包括:

精简指令集:MIPS指令集简洁,指令长度固定,易于硬件实现和编译器优化。

超标量流水线:MIPS架构支持超标量技术,可以在一个时钟周期内同时执行多条指令,提高了处理器的执行效率。

延迟槽:MIPS架构的指令在执行前需要取指令、译码等操作,为了充分利用处理器的资源,MIPS架构引入了延迟槽的概念,即指令的执行结果可以延迟一个时钟周期返回,用于执行下一条指令。

加载/存储架构:MIPS架构采用加载/存储架构,只有加载/存储指令可以访问内存,其他指令需要通过寄存器传递数据。

MIPS架构被广泛应用于路由器、交换机、数字信号处理器等嵌入式系统领域,以及游戏机、工作站等领域。
龙芯的1b,1c都是基于mips32架构的芯片。

1.1 高速缓存

MIPS架构的处理器通常包含多级缓存,其中最常见的是一级指令缓存和数据缓存,以及二级缓存和三级缓存等更大容量的缓存。

一级缓存也称为L1缓存,通常分为指令缓存和数据缓存,分别用于缓存指令和数据。L1缓存通常位于处理器内部,与处理器核心相连,访问速度非常快,通常能在一个时钟周期内访问缓存。

二级缓存也称为L2缓存,通常位于处理器内部或者处理器外部,并且与处理器核心相连。相比于L1缓存,L2缓存容量更大,但访问速度较慢,通常需要数个时钟周期来完成访问。

三级缓存通常称为L3缓存,它的容量更大,位于处理器外部,并且与处理器核心连接。L3缓存的访问速度比L2缓存更慢,但比主内存要快很多,可以用于缓存大量的数据和指令,以减少对主内存的访问次数。

MIPS架构的缓存通常采用哈希表或者相联存储器的形式进行地址映射,同时采用替换算法(如LRU)来处理缓存溢出的情况,以保证缓存的命中率和缓存性能。

1.1.1 术语说明

写回:write back 将cache中的数据,重新写到主存中。如网络发送数据时候,先写到cache,然后执行写回操作,在发送数据。
失效:invailidate:数据清除cache,下次引用时,会从内存中重新读取。

硬件什么时候操作写回?

2 高速缓存访问流程

MIPS架构的高速缓存通常采用哈希表或相联存储器的方式进行地址映射,实现快速的地址查找,其访问流程一般如下:

地址解码:处理器从指令中获取要访问的内存地址,并将其传递给高速缓存控制器。

地址匹配:高速缓存控制器将解码后的地址与缓存中的地址进行比较,以判断是否存在缓存中。

缓存命中:如果缓存中存在该地址对应的数据,则高速缓存控制器将数据从缓存中取出,并返回给处理器,完成访问。

缓存未命中:如果缓存中不存在该地址对应的数据,则高速缓存控制器需要从主存中读取数据,并存储到缓存中,然后返回给处理器,完成访问。

在访问过程中,如果高速缓存已满,会发生缓存溢出的情况,此时高速缓存控制器需要采用替换算法来选择合适的缓存行进行替换,以确保缓存的使用效率。常见的替换算法包括LRU(最近最少使用)、FIFO(先进先出)和随机替换等。
cache访问流程
在这里插入图片描述
Cache和内存之间的传输总是以16字节或32字节对齐的内存块作为传输单元。
即使CPU只是读取一个字节,仍然会加载这样的内存块到Cache行中。

2.1 cache和主存不一致的原因

缓存和主存不一致的原因可能有以下几种:

CPU修改数据:当CPU修改某个数据时,会先在缓存中修改,然后在某个时刻将修改后的数据写回主存。如果在这个时刻之前,DMA或其他设备读取了这个数据,就会读到缓存中的旧数据,导致缓存和主存中的数据不一致。

DMA修改数据:与CPU类似,DMA也可以访问缓存,并在某个时刻将修改后的数据写回主存。如果在这个时刻之前,CPU或其他设备访问了这个数据,就会读到缓存中的旧数据,导致缓存和主存中的数据不一致。

缓存失效:当CPU访问某个数据时,如果这个数据不在缓存中,就需要从主存中读取。如果之前的缓存中的数据已经失效,但还未被写回主存,那么就会读取到主存中的旧数据,导致缓存和主存中的数据不一致。

多级缓存:在多级缓存系统中,如果L1缓存和L2缓存中的数据不一致,就可能导致缓存和主存中的数据不一致。
因此,为了保证系统的正确性和可靠性,需要采取一些措施来管理缓存和主存之间的数据一致性,如使用缓存一致性协议、缓存写回策略等。

2.2 DMA管理中一致性问题

在MIPS架构中,在使用DMA传输数据时,需要特别注意传输的数据的一致性,以避免出现数据不一致的问题。

当DMA控制器与主存交换数据时,由于数据可能会存在缓存中,可能会出现主存和缓存中数据不一致的情况。因此,在使用DMA传输数据时,软件需要注意以下几点:

DMA传输数据前,需要将待传输数据所在的缓存行从缓存中刷出,以保证待传输的数据和主存中的数据一致。

DMA传输完成后,需要对已传输的数据所在的缓存行进行更新,以保证缓存中的数据和主存中的数据一致。

如果在传输数据期间需要修改数据所在的缓存行,需要在修改前先将该缓存行从缓存中刷出,以保证修改后的数据与主存中的数据一致。

如果使用Write-Back方式的高速缓存,还需要注意数据写回的时机,以避免在缓存中的数据被覆盖前未写回到主存而导致数据丢失。

因此,软件在使用DMA传输数据时需要考虑缓存与主存之间的数据一致性,并且需要对传输数据前后的缓存行进行正确的处理,以确保数据传输的正确性和一致性。

3 cache写回(write-back)策略

3.1 什么情况需要写回?

在MIPS32中,写回操作是指将被修改过的脏数据从缓存中写回到主存中。一般来说,MIPS32中的写回操作可以分为以下几种情况:

Cache替换策略:当缓存需要替换一部分数据时,被替换的数据会被写回到主存。

写操作:当CPU执行写操作时,将修改的数据写入缓存中,同时标记该缓存行为"dirty",表示该缓存行的数据已被修改。

缓存刷新:当执行缓存刷新指令(如CacheWriteBack)时,会将所有被修改过的缓存行写回到主存中。

缓存失效:当执行缓存失效指令(如CacheInvalidate)时,会将被失效的缓存行写回到主存中(如果该缓存行被修改过)。

需要注意的是,在执行写回操作时,需要先判断该缓存行是否已被修改过(即是否是dirty)。如果是,则需要将其写回到主存中,以确保数据的完整性。同时,为了提高效率,MIPS32中的缓存通常采用写回策略,即只有在缓存需要被替换或者被失效时才会进行写回操作,而不是每次执行写操作都进行写回操作。

3.2 如何写回

在 MIPS32 中,当一个缓存行被修改过后,它就被标记为“脏”的状态。当需要将这个脏的缓存行写回到主存中时,MIPS32 中的写回操作分为两种情况:

写回并且不需要失效(WriteBack)。当需要将一个脏的缓存行写回到主存中,但是不需要失效它,可以使用 MIPS32 汇编指令 c0(cache 操作指令),操作码为 0x08,功能号为 0x01,这个指令的格式为:c0 op, offset(base),其中 op 的取值为 0x2,表示写回操作,offset 表示缓存行的地址偏移,base 表示缓存行的起始地址。

写回并且失效(WriteBack and Invalidate)。当需要将一个脏的缓存行写回到主存中,并且失效它,可以使用 MIPS32 汇编指令 c0(cache 操作指令),操作码为 0x08,功能号为 0x05,这个指令的格式为:c0 op, offset(base),其中 op 的取值为 0x2,表示写回操作,offset表示缓存行的地址偏移,base 表示缓存行的起始地址。

需要注意的是,在进行写回操作之前,应该先使用 MIPS32 汇编指令sync 将写缓冲区中的数据刷新到主存中,以保证数据的一致性。

4 指令说明

MIPS32指令集架构中提供了一些操作cache的指令,包括:

CACHE 指令:可以用于对一个地址范围内的缓存进行控制,包括使缓存行有效/无效,清空/刷出缓存等。

ERET 指令:在从异常处理返回时,可以用于清空指令/数据缓存,确保缓存中的内容不会影响到新的指令执行。

SYNC 指令:用于同步存储器和缓存,确保数据已经被写入到缓存或存储器中。

TLBP 指令:用于查询TLB中一个特定的入口,同时可以用来强制一个缓存行失效。

TLBWI 和 TLBWR 指令:用于将一个新的页表项写入到TLB中,同时可以用来强制一个缓存行失效。

TLBWR 指令:用于将一个新的页表项写入到TLB中,同时可以用来强制一个缓存行失效。

TLBWI 指令:用于将一个新的页表项写入到TLB中,同时可以用来强制一个缓存行失效。

TLBWR 指令:用于将一个新的页表项写入到TLB中,同时可以用来强制一个缓存行失效。

4.1 sync使用

在 MIPS 指令集架构中,sync 指令用于同步缓存和内存之间的数据,确保在写缓存之后再写内存,从而保证缓存和内存中的数据一致性。它有以下两个主要的使用场景:

写缓存中的数据到内存:在写缓存中的数据后,如果需要将缓存中的数据立即写回到内存中,可以使用 sync 指令来确保写回已经完成。例如,当要将缓存中的数据同步到共享内存中时,可以使用如下指令:

sw $t0, 0($t1)   # 将 $t0 寄存器中的值存入缓存中的地址 $t1
sync             # 确保缓存中的数据已经写回内存

刷新指令缓存:在修改指令后,为了确保下次读取到的是最新的指令,需要将修改后的指令刷新到指令缓存中。可以使用 sync 指令来强制指令缓存刷新,如下所示:

add $t0, $t1, $t2  # 修改指令
sync               # 刷新指令缓存

在软件编写时,建议在修改缓存中的数据后及时调用 sync 指令,以确保缓存和内存中的数据一致性,避免数据异常的情况发生。同时,需要注意,由于 sync 操作会影响处理器的性能,因此不要滥用 sync 指令,只在必要时使用。

4.2 cache指令说明

00000 Index Invalidate              INDEX_INVALIDATE_I          (I)
00001 Index WriteBack Invalidate    INDEX_WRITEBACK_INV_D       (D)
00101 Index Load Tag                INDEX_LOAD_TAG_D            (D)
01001 Index Store Tag               INDEX_STORE_TAG_D           (D)
10001 Hit Invalidate                HIT_INVALIDATE_D            (D)
10101 Hit WriteBack Invalidate      HIT_WRITEBACK_INV_D         (D)
// R10000-specific cacheops
11001 Index Load Data               INDEX_LOAD_DATA_D           (D)
11101 Index Store Data              INDEX_STORE_DATA_D          (D)
00011 Index WriteBack Invalidate    INDEX_WRITEBACK_INV_S(/D)   (S)
00111 Index Load Tag                INDEX_LOAD_TAG_S(/D)        (S)
01011 Index Store Tag               INDEX_STORE_TAG_S(/D)       (S)
10011 Hit Invalidate                HIT_INVALIDATE_S(/D)        (S)
10111 Hit WriteBack Invalidate      HIT_WRITEBACK_INV_S(/D)     (S)
11011 Index Load Data               INDEX_LOAD_DATA_S           (S)
11111 Index Store Data              INDEX_STORE_DATA_S          (S)

1.3.1 Index WriteBack Invalidate (S)

Index WriteBack Invalidate(IWBINV)是一种MIPS架构中的缓存指令,用于将指定索引处的缓存行从缓存中写回到主存并使其无效。

具体来说,这条指令会按照给定的索引,在缓存中查找与之对应的缓存行,并将其中被修改过的数据写回到主存中。同时,该指令还会将缓存行置为无效状态,这意味着缓存中的数据不再可用,后续访问该地址时需要从主存中读取数据。

IWBINV指令一般用于保证缓存和主存数据的一致性。在DMA发送数据前,需要先将待发送数据所在的缓存行从缓存中刷出并使其无效,以确保DMA发送的数据与主存中的数据保持一致。类似地,在对于DMA接收数据后,需要使用IWBINV指令将缓存行设置为有效,以保证后续访问该地址时能够从缓存中获取最新数据。

下面是一个IWBINV指令的示例,假设需要将索引为i的缓存行写回到主存并使其无效:

    .set    noreorder
    .set    noat

    # 将索引i左移5位并加上标志位0x18,得到IWBINV指令的操作码
    li      $at, ((i << 5) | 0x18)
    # 将操作码存入C0协处理器的16号寄存器
    mtc0    $at, $16
    # 执行IWBINV指令
    cache   0x1, 0($index)

在上面的示例中,$index是一个指向索引为i的缓存行的指针。首先将操作码通过mtc0指令写入C0协处理器的寄存器中,然后使用cache指令执行IWBINV操作。这样就可以将该缓存行从缓存中写回到主存并使其无效。

1.3.2 Hit WriteBack Invalidate (S)

Hit WriteBack Invalidate是一种缓存写回策略,在缓存一致性协议中常用。当CPU写入一个缓存行时,如果该缓存行已经在缓存中,并且是修改过的(dirty),则执行该策略:将该缓存行写回到主存,同时将该缓存行标记为无效(invalid)。这样,在之后的访问中,如果发现缓存中的数据被修改过,就会重新从主存中读取最新的数据,以保证数据的一致性。

需要注意的是,如果该缓存行没有被修改过,那么不需要写回到主存,而是可以直接将该缓存行标记为无效。这样可以避免不必要的主存访问,提高访问效率。

1.3.3 Index Load Tag

Index Load Tag (ILT) 指令用于将一个给定地址的数据加载到缓存中,并将标记存储在指定的寄存器中,以供后续的缓存访问使用。

指令格式如下:

ilt rt, offset(rs)

其中,rt 是目标寄存器,rs 是基址寄存器,offset 是偏移量。

该指令会先从地址 offset(rs) 所在的缓存行中读取数据,并将该缓存行的标记存储在 rt 中。如果缓存行不存在,则从主存中读取该缓存行,并将其存储在缓存中。

ILT 指令通常用于在程序中实现缓存操作的一些高级特性,例如自定义的缓存替换算法、预取等。

1.3.3 Index Store Tag

Index Store Tag (IST) 的作用是将一个指定寄存器中的标记存储到指定地址的缓存行中。

指令格式如下:

ist rt, offset(rs)

其中,rt 是包含标记的寄存器,rs 是基址寄存器,offset 是偏移量。

当执行 IST 指令时,CPU 会首先检查缓存中是否存在地址 offset(rs) 所在的缓存行。如果该缓存行存在,则将 rt 中的标记存储到该缓存行的标记字段中。如果该缓存行不存在,则先从主存中加载该缓存行,然后再存储标记。

IST 指令通常用于一些高级的缓存操作,例如自定义的缓存替换算法、预取等。

(待续)