数字芯片设计——握手与反压

前言:在芯片设计或者FPGA设计过程中,流水设计是经常用到的,但是考虑数据安全性,需要与前后级模块进行握手通信,这时候就需要对流水数据进行反压处理,本文将具体介绍握手与反压。

目录

 

握手协议

握手与反压

反压

不带存储体的反压

举个例子——字节的问题

分析与代码

带存储体的反压

逐级反压与跨级反压


握手协议

本文讲述valid-ready握手,下面列出三种握手情况,目的是解释清楚握手的时序。

  • valid先发起请求
数字芯片设计——握手与反压
  • ready先发起请求
数字芯片设计——握手与反压
  • 同时发起请求
数字芯片设计——握手与反压
  • 分析

仔细观察上述3幅时序图,我们了解valid-ready握手机制需要注意三件事:

  1. valid与ready不可过度依赖,比如valid不可以等待ready到达再拉高,ready也不可以等待valid到达再拉高,他俩彼此可以互相独立发出请求拉高;
  2. valid拉高时与有效数据同步,时钟要对齐;
  3. 当数据计算好后,valid可以拉高等待ready拉高,但是每当握手成功之后,数据需要更新,如果此时没有新的有效数据,valid要拉低。

握手与反压

当入口流量大于出口流量,这时候就需要反压,或者,当后级未准备好时,如果本级进行数据传递,那么它就需要反压前级,所以此时前级需要将数据保持不动,直到握手成功才能更新数据。而反压在多级流水线中就变得稍显复杂,原因在于,比如我们采用三级流水设计,如果我们收到后级反压信号,我们理所当然想反压本级输出信号的寄存器,但是如果只反压最后一级寄存器,那么会面临一个问题,就是最后一级寄存器数据会被前两级流水冲毁,导致数据丢失,引出数据安全问题,所以我们此时需要考虑反压设计。

反压

常用的反压方法有三种:

  • 不带存储体的反压

也就是后级反压信号对本级模块中所有流水寄存器都进行控制,由于不包含存储体,为了保证数据安全性,后级反压信号可以同时反压本模块中所有流水寄存器。

优点:节省面积资源

缺点:寄存器端口控制复杂

适用情况:流水线深度较大时

  • 带存储体的逐级反压

如果流水级数不深,可以在每一需要握手交互模块增加存储体,原理上相当于,如果后级发出反压信号,可以直接对本级流水线数据源头进行反压,其余中间级不需控制,但后级需要包含RAM或FIFO等存储体,可以接收流水,并需设置水线(water line),确定反压时间,防止数据溢出,保证数据安全性。

优点:各级流水寄存器端口控制简单

缺点:需要额外存储体

适用情况:流水线深度较小,每一模块都包含存储体时

  • 带存储体的跨级反压

很多时候在具体设计过程中,颗粒度划分不精细,反压这时候是对模块而言,而不是说模块内部有多少级流水。此外,并不是每一模块都带有存储体。比如,其中可能a模块没有存储体,b模块没有存储体,但ab模块内部还有多级流水,如果c模块有存储体,并且需要反压前级模块,这时候可以选择反压a模块的源头输入数据,然后将ab的流水都存储到带有存储体的c模块,但是如果ab不是都没有存储体的话,就不应该跨级反压,而应该逐级反压,具体原因后续会讲。

优点:控制简单方便

缺点:需要额外存储体,模块间耦合度高

适用情况:某些模块带存储体,某些模块不带存储体时

不带存储体的反压

举个例子——字节的问题

问题:设计一个并行6输入32比特加法器,输出1个带截断的32比特加法结果,要求用三级流水设计,带前后反压。

主要输入:

  1. 6个32bit数据
  2. 上一级的valid_i
  3. 下一级的ready_i

输出:

  1. 1个32bit结果
  2. 给上一级的ready_o
  3. 给下一级的valid_o

分析与代码

这道题考察三级流水设计和反压握手设计,首先我们设计时序,下面是我设计的时序图:

数字芯片设计——握手与反压

其中,右侧abcdef代表输入6个数据,r1代表第一级加法结果寄存器,r2代表第二级加法结果寄存器,r3就代表第三级结果。对于时序图,shakehand代表ready_o和valid_i握手成功,重点是valid_o,在计算出有效数据时需要拉高valid_o(也就是shakehand_ff3时),在下一级的ready_i拉高时握手成功,如果此时valid没有持续有效数据就需要拉低,否则可以持续拉高。具体设计代码如下:

module handshake_ex3 ( 		input  wire  		clk, 		input  wire  		rst, 		input  wire  		valid_i, 		input  wire         ready_i, 		input  wire  [31:0] a, 		input  wire  [31:0] b, 		input  wire  [31:0] c, 		input  wire  [31:0] d, 		input  wire  [31:0] e, 		input  wire  [31:0] f, 		output wire  [31:0] s, 		output wire         ready_o, 		output reg          valid_o 	); 	 	wire shakehand; 	assign ready_o	 = ready_i; 	assign shakehand = ready_o & valid_i;  	reg shakehand_ff1; 	reg shakehand_ff2; 	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			shakehand_ff1 <= '0; 			shakehand_ff2 <= '0; 		end 		else begin 			shakehand_ff1 <= shakehand; 			shakehand_ff2 <= shakehand_ff1; 		end 	end  	reg [31 : 0] r1_ab; 	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			r1_ab <= '0; 		end 		else if(shakehand)begin 			r1_ab <= a + b; 		end 	end  	reg [31 : 0] r1_cd; 	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			r1_cd <= '0; 		end 		else if(shakehand)begin 			r1_cd <= c + d; 		end 	end  	reg [31 : 0] r1_ef; 	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			r1_ef <= '0; 		end 		else if(shakehand)begin 			r1_ef <= e + f; 		end 	end		  	reg [31 : 0] r2_abcd; 	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			r2_abcd <= '0; 		end 		else if(shakehand_ff1) begin 			r2_abcd <= r1_ab + r1_cd; 		end 	end  	reg [31 : 0] r2_ef; 	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			r2_ef <= '0; 		end 		else if(shakehand_ff1) begin 			r2_ef <= r1_ef; 		end 	end  	reg [31 : 0] r3; 	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			r3 <= '0; 		end 		else if(shakehand_ff2) begin 			r3 <= r2_ef + r2_abcd; 		end 	end  	always @ (posedge clk or posedge rst) begin 		if(rst) begin 			valid_o <= '0; 		end 		else if(shakehand_ff2) begin 			valid_o <= 1; 		end 		else if(ready_i) begin 			valid_o <= 0; 		end 	end  	assign s = r3; endmodule

对上述代码进行测试,写testbench,测试代码如下:

`timescale 1ns/1ps  module tb_handshake_ex3 (); /* this is automatically generated */      // clock     logic clk;     initial begin         clk = '0;         forever #(0.5) clk = ~clk;     end      // (*NOTE*) replace reset, clock, others      logic        valid_i;     logic        ready_i;     logic [31:0] a;     logic [31:0] b;     logic [31:0] c;     logic [31:0] d;     logic [31:0] e;     logic [31:0] f;     logic [31:0] s;     logic        ready_o;     logic        valid_o;     logic rst;      handshake_ex3 inst_handshake_ex3         (             .clk     (clk),             .rst     (rst),             .valid_i (valid_i),             .ready_i (ready_i),             .a       (a),             .b       (b),             .c       (c),             .d       (d),             .e       (e),             .f       (f),             .s       (s),             .ready_o (ready_o),             .valid_o (valid_o)         );      task init();         valid_i <= '0;         ready_i <= '0;         a       <= '0;         b       <= '0;         c       <= '0;         d       <= '0;         e       <= '0;         f       <= '0;         rst     <= 1;         repeat(2)@(posedge clk);         rst     <= 0;     endtask        initial begin         // do something         init();         repeat(2)@(posedge clk);         valid_i = 1;         a       = 32'd1;         b       = 32'd2;         c       = 32'd3;         d       = 32'd4;         e       = 32'd5;         f       = 32'd6;         @(posedge clk);         ready_i = 1;         @(posedge clk);         ready_i = 0;         a       = 32'd2;         b       = 32'd3;         c       = 32'd4;         d       = 32'd5;         e       = 32'd6;         f       = 32'd7;                 repeat(4)@(posedge clk);         ready_i = 1;         @(posedge clk);         ready_i = 0;             end   endmodule

仿真结果波形如下:

数字芯片设计——握手与反压

对应RTL网表如下:

数字芯片设计——握手与反压

带存储体的反压

如上文所述,很多时候我们不喜欢对每一级细分流水都进行反压,所以可以选择带存储体的反压,也就是增加RAM或者FIFO,在反压上级模块的同时,本级有足够的深度来存储上一级的流水数据。具体内容如下图所示:

数字芯片设计——握手与反压

此时我们假设车库就是最后一级模块,需要对前一级进行反压,车库是带有存储体的模块,那么此时就需要设计水线waterline,也就是当waterline为多少时开始反压前一级模块(车库入口闸机)。由上图可知,当waterline最大为90时,必须向上一模块发出反压请求,因为在途的还有10辆车,这个就相当于我们设计的10级流水,其中有10级寄存器有流水数据输出,所以车库的剩余容量还可以存储住途中流水,保证了数据安全。

逐级反压与跨级反压

这时候的反压包括逐级反压和跨级反压,具体区别可以参考下图:

数字芯片设计——握手与反压

由上图可见,3个模块都包含存储体,我们假设此时module3到达了它的水线,那么它有两种方式反压前面的模块,一种是从最源头进行反压,另一种是逐级反压。

建议:当每一模块都有存储体时,建议逐级反压。

原因:如果逐级反压的话,方法就是module3到达水线则反压module2,module2到达水线反压module1,每一级的水线和存储体大小设计就如上文车库模型所述,简单清晰。但是,如果选择跨级反压,那么module3的水线waterlie3 = 在途1 + waterline1 + 在途2 + waterline2 + 在途3,可见每一级的存储体会变大并且水线计算复杂,另外路径变长,模块间耦合度增高,不利于复用与维护。但是,如果不是每一模块都包含存储体,那么可以选择跨级反压。

---------------------------------------------------------END--------------------------------------------------------

致谢:由于本人电脑不能仿真,感谢乔年的代码coding。

版权声明:玥玥 发表于 2021-03-20 7:06:29。
转载请注明:数字芯片设计——握手与反压 | 女黑客导航