搜索
bottom↓
回复: 5

FPGA SOC 最小面积 的四路组相联指令Cache FIFO置换模式原理和实现

[复制链接]

出0入442汤圆

发表于 2020-7-29 21:51:19 | 显示全部楼层 |阅读模式
RT,今天早上顿悟,迅速搞定了N-Way FIFO更新模式的组相联Cache,实测命中率比直接映射ICache高了一大截,尽管是个FIFO更新模式的只有1KB的Cache(每组256B)。这个是Tight Coupled Cache实现,0T延迟,设计重点是面积最小化,因此整个SOC速度自然不高。如果想要CPU速度跑起来,总线和CPU和Cache等单元需要仔细设计了,全局单一时钟绑定是最简单的实现方式,而且通常情况下FPGA并没有CPU倍频的需求。适合自己组装SOC的用户使用,直接挂到IBus后面。不支持DBus,因为没有数据写端口。

考虑到面积要最小化(后面还有一大堆数据流模块,给SOC的只有微小的一丢丢面积),这里使用了32位宽度的Cache和RAM。考虑到硬件限制,使用了4组多位64x1的RAM单元,更新方式使用FIFO模式,当Cache命中时不更新Cache内容,否则才更新FIFO内容。且Cache是级联的,因此数据总是从最低的组往最高的组移动,这样实现了极简单的多路组相联模型。看代码可以看到,所有RAM写使能是同一个信号,iff cache未命中时才使能。因此,该cache不具备任何LRU等置换能力,毕竟面积优先。

考虑到当前使用的CPU架构实现,只有最低64KB的RAM空间是rwx的,因此只对低16位地址做缓存。级联RAM时直接顺着代码向下扩展,然后在上面ihit部分填好即可。经测试,2-Way和4-Way模式工作良好,可以满足设计需求,全部4-Way在CPU运行一段时间之后全部参与命中。不足之处是Cache无法invalidate,且万一无效数据被填充将导致Cache错误,这个目前没有考虑解决(需要增加不少面积 )。为了避免上电死机的情况发生,当复位使能时,强制刷新Cache内容,避免意外的无效数据。

如果需要和CPU分离开,则需要(1)加宽数据位宽,此时相当于增加了IF的发射宽度,可以最大化提升IPC;(2)增加L2 Cache,并加入数据预读单元,此时SOC才具备在DDR中运行的能力。像这种TCC实现不适合DDR等场合,延迟太大了,命中失败一次至少得300ns~1000ns响应时间,扛不住。由于TCC 0T架构先天不足,Cache模块不具备数据预读能力(只是个单端口的),也不具备多核交互能力。

下面的模块体使用Synplify 2015综合生成verilog netlist只使用了245个LUT6,DFF用了0个 。这个资源使用率是我之前都不敢想的。也就在我上一次顿悟了多行数据缓存器的移位移行操作之后才开始有一些新的设计思想。该多行数据缓存器的目标是从单端口输入的帧数据中生成NxN的数据块,并且数据需要具备边沿映射模式、行结束填充和帧结束填充。最终实现时使用了一个非常简单的方法:生成一个N行的buffer,每次只向最后一行buffer写数据,同时从每一行buffer读出当前数据推到上一行。这样在推完N行数据之后一定能同时输出N+1行数据。再套一个模块实现数据镜像和帧结束填充即解决了大buffer的缓存问题。在这一次顿悟之前我使用了非常复杂的buffer index映射方式,这个方式需要写大量复杂的mux,而且只能实现3x3的buffer缓存,我写了几个星期也没有写出来没有bug的5x5的缓存控制器。而有一个算法期望23x23的缓存

module TinyICache4Way #(
        parameter        INSTR_ADDR_BITS        = 16,                //        64KB memory to be cached
                        CACHE_ADDR_BITS        = 6,                //        64*32 cache RAM to be used.
                        USE_REMAP_REGION        = 0,                //        Set to use remap region (for RWX). Bit [26] (0x04000000).
                        REMAP_REGION_BIT        = 26                //        Bit [26] as remap region address bit.
)(
        input                         clk_i,
        input                         rst_i,
       
        input         [31:0]         iaddr_i,                //        Only INSTR_ADDR_BITS bits used for address.
        input                                ird_i,                //        Instruction Read Strobe.
        output         [31:0]         idata_o,
        output                         iready_o,
       
        input         [31:0]         extdata_i,
        input                         extready_i
);
       

        //        To achieve best performance, the cache memory should contains the following items:
        //        iaddr_i[CACHE_ADDR_BITS+1:2] is to be used for cache selection;
        //        iaddr_i[INSTR_ADDR_BITS-1:CACHE_ADDR_BITS+2] is to be used for cache hit match;
        //        1 bit for cache valid flag.
       
        localparam        CACHE_PAGE_BITS        = USE_REMAP_REGION + INSTR_ADDR_BITS - CACHE_ADDR_BITS - 2;        //        For default configuration the cache page bits should be 14 - 6 - 2 = 6 bits (32 bit access).
        localparam        TOTAL_CACHE_BITS        = 32 + 1 + CACHE_PAGE_BITS;
       
        //        ICache Hit. When hit, do not update cache content.
        wire                        ihit_o;
       
        //        Cache write data. Will include remap region bit at Msb.
        wire        [CACHE_PAGE_BITS-1:0]        w_cache_wepagedata = {iaddr_i[REMAP_REGION_BIT], iaddr_i[INSTR_ADDR_BITS-1:CACHE_ADDR_BITS+2]};
        wire        [TOTAL_CACHE_BITS-1:0]        w_cache_wedata = {extready_i, w_cache_wepagedata, extdata_i[31:0]};
        //        Force flush cache when not ready to avoid invalid data.
        wire                                        w_cache_we = ((~rst_i) && ihit_o) ? 0 : extready_i;
       
        wire        [TOTAL_CACHE_BITS-1:0]        w_cache_odata_0, w_cache_odata_1, w_cache_odata_2, w_cache_odata_3;
       
        //        Assert iready_o when (1) external ready, (2) cache is enabled; cache is valid; cache region match; cache address match; address [1:0] is 0.
        wire                        ihit_0 = w_cache_odata_0[TOTAL_CACHE_BITS-1] && (w_cache_odata_0[TOTAL_CACHE_BITS-2:32] == w_cache_wepagedata);
        wire                        ihit_1 = w_cache_odata_1[TOTAL_CACHE_BITS-1] && (w_cache_odata_1[TOTAL_CACHE_BITS-2:32] == w_cache_wepagedata);
        wire                        ihit_2 = w_cache_odata_2[TOTAL_CACHE_BITS-1] && (w_cache_odata_2[TOTAL_CACHE_BITS-2:32] == w_cache_wepagedata);
        wire                        ihit_3 = w_cache_odata_3[TOTAL_CACHE_BITS-1] && (w_cache_odata_3[TOTAL_CACHE_BITS-2:32] == w_cache_wepagedata);
        assign ihit_o = ihit_0 | ihit_1 | ihit_2 | ihit_3;
       
        assign iready_o = extready_i | ihit_o;
       
        reg        [31:0]        w_idata_o;
        always @(*) begin
                if(ihit_0)                w_idata_o <= w_cache_odata_0[31: 0];
                else if(ihit_1)        w_idata_o <= w_cache_odata_1[31: 0];
                else if(ihit_2)        w_idata_o <= w_cache_odata_2[31: 0];
                else if(ihit_3)        w_idata_o <= w_cache_odata_3[31: 0];
                else                         w_idata_o <= extdata_i;
        end
        assign idata_o = w_idata_o;

        //////////////////////////////////////////////////////////////////////////////////
        //        RAM
        //////////////////////////////////////////////////////////////////////////////////
       
        DistRAM64x8SP #(.GEN_SIZE(TOTAL_CACHE_BITS), .ADDR_BITS(CACHE_ADDR_BITS)) cache_ram_0 (
                .clk_i        (clk_i),
                .addr_i        (iaddr_i[31:2]),                //        Common Address Input
                .wdata_i        (w_cache_wedata),
                .we_i                (w_cache_we),
                .rdata_o        (w_cache_odata_0)
        );
        DistRAM64x8SP #(.GEN_SIZE(TOTAL_CACHE_BITS), .ADDR_BITS(CACHE_ADDR_BITS)) cache_ram_1 (
                .clk_i        (clk_i),
                .addr_i        (iaddr_i[31:2]),                //        Common Address Input
                .wdata_i        (w_cache_odata_0),
                .we_i                (w_cache_we),
                .rdata_o        (w_cache_odata_1)
        );
        DistRAM64x8SP #(.GEN_SIZE(TOTAL_CACHE_BITS), .ADDR_BITS(CACHE_ADDR_BITS)) cache_ram_2 (
                .clk_i        (clk_i),
                .addr_i        (iaddr_i[31:2]),                //        Common Address Input
                .wdata_i        (w_cache_odata_1),
                .we_i                (w_cache_we),
                .rdata_o        (w_cache_odata_2)
        );
        DistRAM64x8SP #(.GEN_SIZE(TOTAL_CACHE_BITS), .ADDR_BITS(CACHE_ADDR_BITS)) cache_ram_3 (
                .clk_i        (clk_i),
                .addr_i        (iaddr_i[31:2]),                //        Common Address Input
                .wdata_i        (w_cache_odata_2),
                .we_i                (w_cache_we),
                .rdata_o        (w_cache_odata_3)
        );
       
       
        //////////////////////////////////////////////////////////////////////////////////
        //        Cache Miss Evaluation
        //////////////////////////////////////////////////////////////////////////////////
        reg        [63:0]        rc_cache_total = 0, rc_cache_miss = 0;
        wire        [63:0]        rc_cache_hit = rc_cache_total - rc_cache_miss;
        always @(posedge clk_i or posedge rst_i) begin
                if(rst_i) begin
                        rc_cache_total <= 0;
                        rc_cache_miss <= 0;
                end else begin
                        rc_cache_miss <= rc_cache_miss + w_cache_we;
                        rc_cache_total <= rc_cache_total + ird_i;
                end
        end

endmodule

module DistRAM64x8SP #(
        parameter        GEN_SIZE        = 8,
                        ADDR_BITS        = 6
)(
        input                                        clk_i,
        input                [ADDR_BITS-1:0]        addr_i,        //        Common Address Input
        input                [GEN_SIZE-1:0]        wdata_i,
        input                                        we_i,
        output        [GEN_SIZE-1:0]        rdata_o
);
       
        genvar i;
       
        generate
                if(ADDR_BITS == 6) begin: Gen_RAM64
                        for(i = 0; i < GEN_SIZE; i = i + 1) begin: Gen_RAM
                                RAM64X1S #(.INIT(64'h0000000000000000)) ram64 (
                                        .O                (rdata_o[i]),        // 1-bit data output
                                        .A0                (addr_i[0]),        // Address[0] input bit
                                        .A1                (addr_i[1]),        // Address[1] input bit
                                        .A2                (addr_i[2]),        // Address[2] input bit
                                        .A3                (addr_i[3]),        // Address[3] input bit
                                        .A4                (addr_i[4]),        // Address[4] input bit
                                        .A5                (addr_i[5]),         // Address[5] input bit
                                        .D                (wdata_i[i]),        // 1-bit data input
                                        .WCLK                (clk_i),                // Write clock input
                                        .WE                (we_i)                // Write enable input
                                );
                        end
                end else if(ADDR_BITS == 7) begin: Gen_RAM128
                        for(i = 0; i < GEN_SIZE; i = i + 1) begin: Gen_RAM
                                RAM128X1S #(.INIT(128'h00000000000000000000000000000000)) ram128 (
                                        .O                (rdata_o[i]),        // 1-bit data output
                                        .A0                (addr_i[0]),        // Address[0] input bit
                                        .A1                (addr_i[1]),        // Address[1] input bit
                                        .A2                (addr_i[2]),        // Address[2] input bit
                                        .A3                (addr_i[3]),        // Address[3] input bit
                                        .A4                (addr_i[4]),        // Address[4] input bit
                                        .A5                (addr_i[5]),         // Address[5] input bit
                                        .A6                (addr_i[6]),         // Address[6] input bit
                                        .D                (wdata_i[i]),        // 1-bit data input
                                        .WCLK                (clk_i),                // Write clock input
                                        .WE                (we_i)                // Write enable input
                                );
                        end
                end else if(ADDR_BITS == 8) begin: Gen_RAM256
                        for(i = 0; i < GEN_SIZE; i = i + 1) begin: Gen_RAM
                                RAM256X1S #(.INIT(256'h0000000000000000000000000000000000000000000000000000000000000000)) ram256 (
                                        .O                (rdata_o[i]),        // 1-bit data output
                                        .A                (addr_i),                // Address[0] input bit
                                        .D                (wdata_i[i]),        // 1-bit data input
                                        .WCLK                (clk_i),                // Write clock input
                                        .WE                (we_i)                // Write enable input
                                );
                        end
                end else begin: Gen_RAM32
                        for(i = 0; i < GEN_SIZE; i = i + 2) begin: Gen_RAM
                                RAM32X2S #(.INIT_00(32'h00000000), .INIT_01(32'h00000000)) ram32 (
                                        .O0                (rdata_o[i + 0]),        // 1-bit data output
                                        .O1                (rdata_o[i + 1]),        // 1-bit data output
                                        .A0                (addr_i[0]),        // Address[0] input bit
                                        .A1                (addr_i[1]),        // Address[1] input bit
                                        .A2                (addr_i[2]),        // Address[2] input bit
                                        .A3                (addr_i[3]),        // Address[3] input bit
                                        .A4                (addr_i[4]),        // Address[4] input bit
                                        .D0                (wdata_i[i + 0]),        // 1-bit data input
                                        .D1                (wdata_i[i + 1]),        // 1-bit data input
                                        .WCLK                (clk_i),                // Write clock input
                                        .WE                (we_i)                // Write enable input
                                );
                        end
                end
        endgenerate

endmodule

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2020-7-29 23:35:13 | 显示全部楼层
说实话没听懂。但是觉得好厉害的样子。

出0入42汤圆

发表于 2020-7-30 00:09:31 来自手机 | 显示全部楼层
楼主fpga大牛啊,这些功能一般都用在图像处理上吗

出0入442汤圆

 楼主| 发表于 2020-7-30 06:18:11 来自手机 | 显示全部楼层
我是一个大白菜 发表于 2020-7-30 00:09
楼主fpga大牛啊,这些功能一般都用在图像处理上吗

这个模块是cpu指令cache。后面提到的多行缓存是图像上用的。

出0入42汤圆

发表于 2020-7-30 07:46:06 来自手机 | 显示全部楼层
谢谢指导,先理解一下这个方法

出0入4汤圆

发表于 2020-7-30 13:43:50 来自手机 | 显示全部楼层
所有字都认得,放一块儿不认得系列……
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 19:07

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表