玩命加载中 . . .

在动态分配内存时,操作系统必须对其进行管理。有两种方式跟踪内存使用情况:位图和空闲链表。

  1. 使用位图的存储管理

使用位图方法时,内存可能被划分成小到几个字或大到几千字节的分配单元。每个分配单元对应于位图中的一位,0表示空闲,1表示占用(或者相反)。一块内存区和其对应的位图如下图所示。

image-20220517221848784

分配单元的大小是一个重要的设计因素。分配单元越小,位图越大。然而即使只有4个字节大小的分配单元,32位(4字节)的内存也只需要位图中的1位;32n位的内存需要n位的位图,所以位图只占用了1/32的内存。若选择比较大的分配单元,则位图更小。但若进程的大小不是分配单元的整数倍,那么在最后一个分配单元中就会有一定数量的内存被浪费了。

我们知道在芯片设计中,降低动态功耗的方法有很多种。比如clock gating,power gating等。今天想介绍一个通过减小移动数据位宽来降低动态功耗的方法。

动态功耗的来源是信号的传播,翻转。clock gating通过减少时钟信号的翻转,降低了寄存器clk pin本身的功耗,并且通过寄存器的特性,间接减少了Q pin的翻转,从而降低了数据链路的功耗。而power gating更绝,直接将电路断电,更无翻转可言。这两种方式本质上都是在电路或者某些寄存器没有数据更新需求的时候减少无谓的信号翻转,以此来降低功耗。而减小传播数据的位宽,可以在有数据更新需求的时候减少动态功耗。毕竟传递的数据少了,需要翻转的信号自然也少了。但是这样做需要考虑上减小数据位宽所带来的一点额外逻辑开销造成的功耗。不过大多数时候都能得到正收益,特别是当数据位宽较大的时候。

今天想分享几种流保序策略。保序问题在不同的应用领域都会遇到,毕竟硬件电路本质上是需要很多数据流传输的。各种协议的接口,处理器的数据处理,指令处理等都离不开对应数据有序的传输。

如果只有一条数据流,可以直接使用一个FIFO保序。如果是多数据流,多个读写口,可以分不同ID使用不同FIFO保序。今天想讨论的是多数据流混合场景下只有1个读口和1个写口的保序。不同的数据流有对应不同的ID,大致有以下5种不同的保序策略。

首先来看一下场景,这是一个在大多数设计中都会遇到的数据包调度问题。有A,B两个输入通道,向数据调度模块DUT输入数据。该调度模块DUT有一个输出通道C。A,B和DUT,DUT和C都是通过简单的握手传输数据。

img

两个输入通道都有数据包输入,可以同时输入。数据包以packet为单位,每个packet为128bit。不同的数据包有不同的id,id是4个bit。A和B通道不会同时输入同ID的数据。每个packet带有2bit的qos,qos越高,可以理解为该id的packet优先级相对于其他id的packet优先级越高。输出通道没有反压,只要有数据就可以出去。输入通道接受反压。数据输出的条件是,同id的packet要保序输出,不同id的packet,优先级较高的需要尽量先输出。

异步fifo代码

module afifo
#(parameter DATA_WIDTH = 64,
            FIFO_DEPTH = 8,
            AF_LEVEL   = (1<<FIFO_DEPTH - 10),
            AE_LEVEL   = 10
)
(
  input                         wclk          ,
  input                         wrst          ,
  input                         wen           ,
  input       [DATA_WIDTH-1:0]  wdata         ,
  output reg                    wfull         ,
  output reg                    walmost_full  ,

  input                         rclk          ,
  input                         rrst          ,
  input                         ren           ,
  output reg  [DATA_WIDTH-1:0]  rdata         ,
  output reg                    rempty        ,
  output reg                    ralmost_empty
);


wire                  fifo_rd;
wire                  fifo_wr;
wire [FIFO_DEPTH-1:0] raddr;
wire [FIFO_DEPTH-1:0] waddr;
reg  [DATA_WIDTH-1:0] memory [0:(1<<FIFO_DEPTH)-1];

wire                  wen_mask;
wire [FIFO_DEPTH:0]   wbinnext;
wire [FIFO_DEPTH:0]   wptrnext;
reg  [FIFO_DEPTH:0]   wbin;
reg  [FIFO_DEPTH:0]   wptr;
reg  [FIFO_DEPTH:0]   wptr_rsync1;
reg  [FIFO_DEPTH:0]   wptr_rsync2;
wire [FIFO_DEPTH:0]   wbin_rsync;

wire                  ren_mask;
wire [FIFO_DEPTH:0]   rbinnext;
wire [FIFO_DEPTH:0]   rptrnext;
reg  [FIFO_DEPTH:0]   rbin;
reg  [FIFO_DEPTH:0]   rptr;
reg  [FIFO_DEPTH:0]   rptr_wsync1;
reg  [FIFO_DEPTH:0]   rptr_wsync2;
wire [FIFO_DEPTH:0]   rbin_wsync;

genvar i;

//*************************************
// Dual port RAM
//*************************************
assign fifo_wr = wen_mask;
always @(posedge wclk) begin
  if(fifo_wr)
    memory[waddr] <= wdata;
end

assign fifo_rd = ren_mask;
always @(posedge rclk) begin
  if(fifo_rd)
    rdata <= memory[raddr];
end
//

//*************************************
// sync wptr to rclk domain
//*************************************
always @(posedge rclk or negedge rrst) begin
  if(rrst)
    {wptr_rsync2,wptr_rsync1} <= 0;
  else
    {wptr_rsync2,wptr_rsync1} <= {wptr_rsync1,wptr};
end

generate
  for(i=FIFO_DEPTH;i>=0;i=i-1) begin: wbin_rsync_gen
    assign wbin_rsync[i] = ^({ {i{1'b0}},wptr_rsync2[FIFO_DEPTH:i]});
  end
endgenerate

//*************************************
// sync rptr to wclk domain
//*************************************
always @(posedge wclk or negedge wrst) begin
  if(wrst)
    {rptr_wsync2,rptr_wsync1} <= 0;
  else
    {rptr_wsync2,rptr_wsync1} <= {rptr_wsync1,rptr};
end

generate
  for(i=FIFO_DEPTH;i>=0;i=i-1) begin: rbin_wsync_gen
    assign rbin_wsync[i] = ^({ {i{1'b0}},rptr_wsync2[FIFO_DEPTH:i]});
  end
endgenerate

//*************************************
// rd addr generate
//*************************************
always @(posedge rclk or negedge rrst) begin
  if(rrst)
    {rbin,rptr} <= 0;
  else
    {rbin,rptr} <= {rbinnext,rptrnext};
end
assign raddr    = rbin[FIFO_DEPTH-1:0];
assign ren_mask = ren & (~rempty);
assign rbinnext = ren_mask ? (rbin + 1'b1) : rbin;
assign rptrnext = (rbinnext>>1) ^ rbinnext;


//*************************************
// wr addr generate
//*************************************
always @(posedge wclk or negedge wrst) begin
  if(wrst)
    {wbin,wptr} <= 0;
  else
    {wbin,wptr} <= {wbinnext,wptrnext};
end
assign waddr = wbin[FIFO_DEPTH-1:0];
assign wen_mask = (wen & (~wfull));
assign wbinnext = wen_mask ? (wbin + 1'b1) : wbin;
assign wptrnext = (wbinnext>>1) ^ wbinnext;

//*************************************
// FIFO status
//*************************************
always @(posedge rclk or negedge rrst) begin
  if(rrst)
    rempty <= 1'b1;
  else
    rempty <= (rptrnext == wptr_rsync2);
end

always @(posedge rclk or negedge rrst) begin
  if(rrst)
    ralmost_empty <= 1'b1;
  else
    ralmost_empty <= (wbin_rsync - rbinnext <= AE_LEVEL);
end

always @(posedge wclk or negedge wrst) begin
  if(wrst)
    wfull <= 0;
  else
    wfull <= (wptrnext == {~rptr_wsync2[FIFO_DEPTH:FIFO_DEPTH-1],rptr_wsync2[FIFO_DEPTH-2:0]});
end

always @(posedge wclk or negedge wrst) begin
  if(wrst)
    walmost_full <= 1'b1;
  else
    walmost_full <= (wbinnext - rbin_wsync >= AF_LEVEL);
end

endmodule