【CPU微架构设计】分布式多端口(4写2读)寄存器堆设计

  寄存器堆(Register File)是微处理的关键部件之一。寄存器堆往往具有多个读写端口,其中写端口往往与多个处理单元相对应。传统的方法是使用集中式寄存器堆,即一个集中式寄存器堆匹配N个处理单元。随着端口数量的增加,集中式寄存器堆的功耗、面积、时序均会呈幂增长,进而可能降低处理器总体性能。

  下图所示为传统的集中式寄存器堆结构:

  本文讨论一种基于分布存储和面积与时序互换原则的多端口寄存器堆设计,我们暂时称之为“分布式寄存器堆”。该种寄存器从端口使用上,仍与集中式寄存器堆完全兼容,但该寄存器堆使用多个寄存器簇和块分布式地存储操作数。当并行写入时,各寄存器簇分布式地存储写入结果;当读出时,由相应的仲裁算法在多个寄存器簇的结果中选取确定的一个簇作为最终输出。图二显示了这种分布式寄存器堆的逻辑结构。

  该结构主要由区块(Section)和簇(Cluster)两个维度组织寄存器:

  (1)、从Section层面看,Section 1和Section 2是两个完全相同的组成结构(相当于逻辑的复制),同时两个Section中寄存器所持有的操作数也完全相同。一个Section只能处理一个读端口的操作。Section1负责处理读端口#1,而Section2负责处理读端口#2。

  (2)、从Cluster层面看,Cluster A、B、C、D是几个分别独立的集中式寄存器堆,其具体结构可等价为一个双口RAM。写入时,Clusters分别独立地写入各自要求的地址。读出时,各Cluster从相同的读出地址读出各自的数据,最后将4个数据送到MUX进行最后的仲裁。

  综上,单个Section负责单个读端口操作,单个Cluster负责单个写端口操作。

  分布式寄存器堆的设计关键在于仲裁调度算法。由于一个Section只负责处理一个读端口的操作,我们首先从单个Section维度考虑。虽然单个周期内需要同时处理4个写端口,但读端口是唯一的。事先记录每个写端口的地址所对应的Cluster编号,在读出时,通过读出地址反向获取对应的Cluster,进而从该Cluster取出最终结果。

  其次再考虑多个读端口并行操作。考虑复制两个相同的Section逻辑,并将所有写端口并联起来,则可以保证两个Section所持有的操作数完全相同。对两个Section同时进行读操作,这样便实现了并行地读出两个不同地址的数据。

接下来设计两个关键部件:Cluster寄存器堆数据仲裁器

一、Cluster寄存器的设计

上文已经提到过,Cluster寄存器可等价为双端口RAM。关于DPRAM的具体结构可参考相关资料,本文不再赘述。DPRAM容量根据最为通用的配置,采用32x32bit设计,可通过例化参数设置地址总线和数据总线的宽度。利用Verilog描述一个同步时钟DPRAM的源码如下:

 1 module dpram_sclk
 2 #(
 3     parameter ADDR_WIDTH = 32,
 4     parameter DATA_WIDTH = 32,
 5     parameter CLEAR_ON_INIT = 1, // Whether to rest the RAM while initialization (for simulation only)
 6     parameter ENABLE_BYPASS = 1  // Whether enable data bypass
 7 )
 8 (/*AUTOARG*/
 9    // Outputs
10    dout,
11    // Inputs
12    clk, rst, raddr, re, waddr, we, din
13    );
14
15    // Port List
16    input                           clk;
17    input                           rst;
18    input [ADDR_WIDTH-1:0]          raddr;
19    input                           re;
20    input [ADDR_WIDTH-1:0]          waddr;
21    input                           we;
22    input [DATA_WIDTH-1:0]          din;
23    output [DATA_WIDTH-1:0]         dout;
24
25    reg [DATA_WIDTH-1:0]            mem[(1<<ADDR_WIDTH)-1:0];
26    reg [DATA_WIDTH-1:0]            rdata;
27    reg                             re_r;
28    wire [DATA_WIDTH-1:0]           dout_w;
29
30    generate
31       if(CLEAR_ON_INIT) begin :clear_on_init
32          integer entry;
33          initial begin
34             for(entry=0; entry < (1<<ADDR_WIDTH); entry=entry+1) // reset
35                mem[entry] = {DATA_WIDTH{1‘b0}};
36          end
37       end
38    endgenerate
39
40    // bypass control
41    generate
42       if (ENABLE_BYPASS) begin : bypass_gen
43          reg [DATA_WIDTH-1:0]  din_r;
44          reg                   bypass;
45
46          assign dout_w = bypass ? din_r : rdata;
47
48          always @(posedge clk)
49             if (re) din_r <= din;
50
51          always @(posedge clk)
52             if (waddr == raddr && we && re)
53                bypass <= 1;
54             else
55                bypass <= 0;
56       end else begin
57          assign dout_w = rdata;
58       end
59    endgenerate
60
61    // R/W logic
62    always @(posedge clk)
63       re_r <= rst ? 1‘b0 : re;
64
65    assign dout = re_r ? dout_w : {DATA_WIDTH{1‘b0}};
66
67    always @(posedge clk) begin
68      if (we)
69          mem[waddr] <= din;
70      if (re)
71          rdata <= mem[raddr];
72    end
73
74 endmodule

module dpram_sclk

值得注意的是,实现中加入了数据旁通机制,保证当发生同时读写且地址相同时,写入数据能在单个操作周期内送达读端口。

二、数据仲裁器的设计

数据仲裁器的核心是维护一个写入地址→Cluster编号的映射表。假设分布式寄存器堆总共有32个寄存器(以5bit地址总线寻址),则我们需要一个表项数为32的列表,存储每个地址对应的Cluster编号。Verilog描述如下:

reg [1:0]                       sel_map[(1<<ADDR_WIDTH)-1:0];

对于每个写端口,在写操作周期维护映射表,记录下写入地址对应的Cluster,实现如下:

 1    // Maintain the selection map
 2    always @(posedge clk) begin
 3       if (we1)
 4          sel_map[waddr1] <= 2‘d0;
 5       if (we2)
 6          sel_map[waddr2] <= 2‘d1;
 7       if (we3)
 8          sel_map[waddr3] <= 2‘d2;
 9       if (we4)
10          sel_map[waddr4] <= 2‘d3;
11    end

对于读端口,先从映射表获取实际存储目标操作数的Cluster,然后利用多路复用器选取其输出,作为该Section的最终读取结果。

1    // mux
2    assign dout = sel_map[raddr]==2‘d0 ? dout0 :
3                  sel_map[raddr]==2‘d1 ? dout1 :
4                  sel_map[raddr]==2‘d2 ? dout2 :
5                  sel_map[raddr]==2‘d3 ? dout3 :
6                  {DATA_WIDTH{1‘b0}}; /* never got this */

由此,我们可以得出单个Section的完整设计:

  1 module cpram_sclk_4w1r #(
  2     parameter ADDR_WIDTH = 5,
  3     parameter DATA_WIDTH = 32,
  4     parameter CLEAR_ON_INIT = 1, // Whether to rest the RAM while initialization (for simulation only)
  5     parameter ENABLE_BYPASS = 1  // Whether enable data bypass
  6 )
  7 (/*AUTOARG*/
  8    // Outputs
  9    rdata,
 10    // Inputs
 11    clk, rst, we1, waddr1, wdata1, we2, waddr2, wdata2, we3, waddr3,
 12    wdata3, we4, waddr4, wdata4, re, raddr
 13    );
 14
 15    // Ports
 16    input                           clk;
 17    input                           rst;
 18    input                           we1;
 19    input [ADDR_WIDTH-1:0]          waddr1;
 20    input [DATA_WIDTH-1:0]          wdata1;
 21    input                           we2;
 22    input [ADDR_WIDTH-1:0]          waddr2;
 23    input [DATA_WIDTH-1:0]          wdata2;
 24    input                           we3;
 25    input [ADDR_WIDTH-1:0]          waddr3;
 26    input [DATA_WIDTH-1:0]          wdata3;
 27    input                           we4;
 28    input [ADDR_WIDTH-1:0]          waddr4;
 29    input [DATA_WIDTH-1:0]          wdata4;
 30    input                           re;
 31    input [ADDR_WIDTH-1:0]          raddr;
 32    output [DATA_WIDTH-1:0]         rdata;
 33
 34    // Internals
 35    wire [DATA_WIDTH-1:0]           dout;
 36    wire [DATA_WIDTH-1:0]           dout0;
 37    wire [DATA_WIDTH-1:0]           dout1;
 38    wire [DATA_WIDTH-1:0]           dout2;
 39    wire [DATA_WIDTH-1:0]           dout3;
 40    reg [1:0]                       sel_map[(1<<ADDR_WIDTH)-1:0];
 41
 42    // instance of sync dpram #1 for Cluster A
 43    dpram_sclk
 44        #(
 45          .ADDR_WIDTH       (ADDR_WIDTH),
 46          .DATA_WIDTH       (DATA_WIDTH),
 47          .CLEAR_ON_INIT    (CLEAR_ON_INIT),
 48          .ENABLE_BYPASS    (ENABLE_BYPASS)
 49        )
 50       mem0
 51        (
 52          .clk          (clk),
 53          .rst          (rst),
 54          .dout         (dout0),
 55          .raddr        (raddr),
 56          .re           (re),
 57          .waddr        (waddr1),
 58          .we           (we1),
 59          .din          (wdata1)
 60        );
 61    // instance of sync dpram #2 for Cluster B
 62    dpram_sclk
 63        #(
 64          .ADDR_WIDTH       (ADDR_WIDTH),
 65          .DATA_WIDTH       (DATA_WIDTH),
 66          .CLEAR_ON_INIT    (CLEAR_ON_INIT),
 67          .ENABLE_BYPASS    (ENABLE_BYPASS)
 68        )
 69       mem1
 70        (
 71          .clk          (clk),
 72          .rst          (rst),
 73          .dout         (dout2),
 74          .raddr        (raddr),
 75          .re           (re),
 76          .waddr        (waddr2),
 77          .we           (we2),
 78          .din          (wdata2)
 79        );
 80    // instance of sync dpram #3 for Cluster C
 81    dpram_sclk
 82        #(
 83          .ADDR_WIDTH       (ADDR_WIDTH),
 84          .DATA_WIDTH       (DATA_WIDTH),
 85          .CLEAR_ON_INIT    (CLEAR_ON_INIT),
 86          .ENABLE_BYPASS    (ENABLE_BYPASS)
 87        )
 88       mem2
 89        (
 90          .clk          (clk),
 91          .rst          (rst),
 92          .dout         (dout3),
 93          .raddr        (raddr),
 94          .re           (re),
 95          .waddr        (waddr3),
 96          .we           (we3),
 97          .din          (wdata3)
 98        );
 99    // instance of sync dpram #4 for Cluster D
100    dpram_sclk
101        #(
102          .ADDR_WIDTH       (ADDR_WIDTH),
103          .DATA_WIDTH       (DATA_WIDTH),
104          .CLEAR_ON_INIT    (CLEAR_ON_INIT),
105          .ENABLE_BYPASS    (ENABLE_BYPASS)
106        )
107       mem3
108        (
109          .clk          (clk),
110          .rst          (rst),
111          .dout         (dout3),
112          .raddr        (raddr),
113          .re           (re),
114          .waddr        (waddr4),
115          .we           (we4),
116          .din          (wdata4)
117        );
118
119    // mux
120    assign dout = sel_map[raddr]==2‘d0 ? dout0 :
121                  sel_map[raddr]==2‘d1 ? dout1 :
122                  sel_map[raddr]==2‘d2 ? dout2 :
123                  sel_map[raddr]==2‘d3 ? dout3 :
124                  {DATA_WIDTH{1‘b0}}; /* never got this */
125
126    // Read output with/without bypass controlling
127    generate
128       if (ENABLE_BYPASS) begin : bypass_gen
129          assign rdata =
130                  (we1 && (raddr==waddr1)) ? wdata1 :
131                  (we2 && (raddr==waddr2)) ? wdata2 :
132                  (we3 && (raddr==waddr3)) ? wdata3 :
133                  (we4 && (raddr==waddr4)) ? wdata4 :
134                  dout;
135       end else begin
136          assign rdata = dout;
137       end
138    endgenerate
139
140    // Maintain the selection map
141    always @(posedge clk) begin
142       if (we1)
143          sel_map[waddr1] <= 2‘d0;
144       if (we2)
145          sel_map[waddr2] <= 2‘d1;
146       if (we3)
147          sel_map[waddr3] <= 2‘d2;
148       if (we4)
149          sel_map[waddr4] <= 2‘d3;
150    end
151 endmodule

值得说明的是:上述实现仍然需要考虑四个写端口与一个读端口的数据旁通路径。通过例化参数ENABLE_BYPASS可以指定是否使用数据旁通逻辑。

将两个Section写端口并联,并分别引出其读端口,即构成了一个4w 2r寄存器堆。

总结

  本文讨论的数据的分布存储方法和基于面积换时序的逻辑复制方法,在集中存储式寄存器堆的优化中取得了较好的效果。但该结构缺点也十分明显:首先,对集中寄存器堆的复制无疑增加了面积和功耗,其次,随着写端口数的增加,仲裁逻辑的规模也随之增长,这将导致数据路径延迟增加,进而降低寄存器堆时钟工作频率。

  相比于ASIC设计,本结构更适合于FPGA验证。理由如下:在FPGA中,寄存器是非常有限的资源。若直接实现一定规模的RAM结构,则需要大量占用寄存器资源。为此,FPGA在硬件上集成了RAM Block资源。这些RAM Blocks多可配置为双端口模式,但对于更复杂的端口配置(如本例的4w2r),则只能间接实现。

  本设计完全采用RAM Blocks实现多端口寄存器堆。因为FPGA综合工具在分析verilog源码时,将自动识别出我们所采用的DPRAM,转而使用RAM Block资源,避免占用寄存器资源,为设计的其它部分留出更多可用的寄存器资源。同时,这将避免使用LUT实现复杂的RAM单元寻址和控制逻辑,从而优化时序。

2018 10.20

===================================================

本博文仅供参考,难免有疏漏之处,欢迎提出宝贵意见。

原文地址:https://www.cnblogs.com/sci-dev/p/9822994.html

时间: 2024-10-10 09:01:46

【CPU微架构设计】分布式多端口(4写2读)寄存器堆设计的相关文章

【CPU微架构设计】利用Verilog设计基于饱和计数器和BTB的分支预测器

在基于流水线(pipeline)的微处理器中,分支预测单元(Branch Predictor Unit)是一个重要的功能部件,它负责收集和分析分支/跳转指令的参数和执行结果,当处理新的分支/跳转指令时,BPU将根据已有的统计结果和当前分支跳转指令的参数,预测其执行结果,为流水线取指提供决策依据,进而提高流水线效率. 下面讨论提出分支预测机制的主要原因和实际意义: 在流水线处理分支跳转指令时,目标地址往往需要推迟到指令的执行阶段才能运算得出,在此之前处理器无法及时得知下一条指令的取指地址,因此无法

ARM架构--CPU的微架构

网上确实有说ARM架构的,但是此架构泛指用ARM指令系统的CPU,而不是CPU的微架构.,硬件电路上,要用ARM指令集系统,必然硬件设计电路上要要遵循,ARM指令的特点和寻址方式,所以说高通和苹果的CPU是ARM架构是可以的. ,但CPU的微架构就要复杂太多了,CPU微架构包括缓存管理,缓存设计,乱序执行,超标量,超流水线,内存控制,分支预测等众多的极其复杂的电路,这些结构的电路的效率是直接决定CPU性能的. 高通和苹果是自主设计的这些电路,所以说是自主的CPU架构,只是用了ARM的指令系统和寻

微架构设计:微博计数器的设计

作者:@cydu 来源: http://qing.weibo.com/1639780001/61bd0ea133002460.html http://qing.weibo.com/1639780001/61bd0ea1330025sq.html 背景:  每一条微博的转发和评论背后都是一串串说不完的故事,但是今天主要讲的是 计数服务,计数服务详尽地记录着每条微博 被评论的次数 和 被转发的次数,当然也还有更多的喜怒哀乐都记录于此. 数据量:   微博总数量:  千亿级 而且每秒都在飞速增长中.每

java微服务架构的分布式事务解决方案

java微服务架构的分布式事务解决方案 课程目录如下: 1.课程介绍20分钟2.解决方案的效果演示(结合支付系统真实应用场景)45分钟3.常用的分布式事务解决方案介绍47分钟4.消息发送一致性(可靠消息的前提保障)20分钟5.消息发送一致性的异常流程处理16分钟6.常规MQ队列消息的处理流程和特点12分钟7.消息重复发送问题及业务接口的幂等性设计18分钟8.可靠消息最终一致性方案1(本地消息服务)的设计19分钟9.可靠消息最终一致性方案2(独立消息服务)的设计24分钟10.可靠消息服务的设计与实

微服务架构及分布式事务解决方案

分布式事务 分布式事务场景如何设计系统架构及解决数据一致性问题,个人理解最终方案把握以下原则就可以了,那就是:大事务=小事务(原子事务)+异步(消息通知),解决分布式事务的最好办法其实就是不考虑分布式事务,将一个大的业务进行拆分,整个大的业务流程,转化成若干个小的业务流程,然后通过设计补偿流程从而考虑最终一致性. What’s 事务 事务(Transaction)及其ACID属性 事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性: 原子性(Atomic

微服务架构的分布式事务场景及解决方案分析

分布式系统架构中,分布式事务问题是一个绕不过去的挑战.而微服务架构的流行,让分布式事问题日益突出! 下面我们以电商购物支付流程中,在各大参与者系统中可能会遇到分布式事务问题的场景进行详细的分析! 如上图所示,假设三大参与平台(电商平台.支付平台.银行)的系统都做了分布式系统架构拆分,按上数中的流程步骤进行分析: 1.电商平台中创建订单:预留库存.预扣减积分.锁定优惠券,此时电商平台内各服务间会有分布式事务问题,因为此时已经要跨多个内部服务修改数据: 2.支付平台中创建支付订单(选银行卡支付):查

微服务架构的分布式事务解决方案

微服务架构的分布式事务解决方案 标签:分布式事务,微服务,消息最终一致性,分布式事务解决方案发布于 2016-07-16 18:39:05 分布式系统架构中,分布式事务问题是一个绕不过去的挑战.而微服务架构的流行,让分布式事问题日益突出! 下面我们以电商购物支付流程中,在各大参与者系统中可能会遇到分布式事务问题的场景进行详细的分析! 如上图所示,假设三大参与平台(电商平台.支付平台.银行)的系统都做了分布式系统架构拆分,按上数中的流程步骤进行分析: 1.电商平台中创建订单:预留库存.预扣减积分.

阿里微服务架构下分布式事务解决方案-GTS

虽然微服务现在如火如荼,但对其实践其实仍处于初级阶段.即使互联网巨头的实践也大多是试验层面,鲜有核心业务系统微服务化的案例.GTS是目前业界第一款,也是唯一的一款通用的解决微服务分布式事务问题的中间件,而且可以保证数据的强一致性.本文将对GTS做出深入解读. 微服务倡导将复杂的单体应用拆分为若干个功能简单的.松耦合的服务,这样可以降低开发难度.增强扩展性.便于敏捷开发.概念2012年提出迅速火遍全球,被越来越多的开发者推崇,很多互联网行业巨头.开源社区等都开始了微服务的讨论和实践.根据Netfl

手机服务器微架构设计和实现专题

9.29 开始动手看着慕课网的视频去学习手机服务器微架构实现,原因是: 现在自己想要认真地学习了解android的基本网络知识,以及其实践 一.知识准备 首先,我要了解tcp.udp和http的关系,了解自己需要在什么情况下选择哪个. 然后,就是我需要了解的是http的实现原理. 据说这个是我们面试时有可能需要考到的,其实,我需要的是弄清楚 request和accept需要做的是什么 最后,是关于http的解析过程. 二.写代码过程 接下来,就是跟着视频写代码 : 首先是设定accept soc