在进行模块设计时,我们经常需要进行数据位宽的转换,常见的两种转换场景有同步时钟域位宽转换和异步时钟域位宽转换。本文将介绍异步时钟域位宽转换
异步时钟域的位宽转换读时钟和写时钟属于两个时钟。如下案例中,数据位宽由32bit转40bit,写时钟频率156.25MHz,读时钟频率125Mhz,写数据为32bit,读数据位宽为40bit,通过计算得到入口数据速率和出口数据速率保持一致(156.25 32==40 125)。
存储模块是由寄存器搭建的。那么需要多大存储模块呢?32和40的最小公倍数为160,极限场景下,只需要160bit的寄存器作为存储就够了,但是读操作通常晚于写操作,并且考虑到时钟有抖动有偏移,为了避免溢出,稍微增加一部分缓存,我们可以采用320bit作为存储模块。因此写侧32bit写10次,读侧40bit读8次,读写两侧所需的时间相等。
注意事项:写地址(wr_addr)跳转范围是0~9,读地址(rd_addr)跳转范围0~7。
如图所示:
buff_array为320bit的数据存储。
vld_array为80bit的有效标志位存储:vld_array[n]为1表示buff_array[4n+3:4n]存在4bit的有效数据。
always @(posedge wr_clk or negedge wr_rst_n) begin
if (~wr_rst_n) begin
buff_array <= {DATA_FIFO_DEPTH{1'b0}};
vld_array <= {VALID_FIFO_DEPTH{1'b0}};
end else begin
if (wr_en) begin
buff_array[ wr_addr*32 +: 32] <= wr_data_i;
vld_array[wr_addr*8 +: 8] <= {8{wr_valid_i}};
end
end
end
reg [10-1:0] rd_valid_bus;
reg [40-1:0] rd_data_bus;
always @(*) begin
rd_data_bus[40-1:0] = buff_array[ rd_addr*40 +: 40];
rd_valid_bus[10-1:0] = vld_array[rd_addr*10 +: 10];
end
integer i;
reg [40-1:0] rd_data_valid_mask;
always @(*) begin
for(i = 0; i < 40; i = i + 1) begin
rd_data_valid_mask[i] = rd_valid_bus[i/4];
end
end
always @(posedge rd_clk or negedge rd_rst_n) begin
if (~rd_rst_n) begin
rd_data_o <= {40{1'b0}};
rd_valid_o <= 1'b0;
end else begin
if (rd_en) begin
rd_data_o <= rd_data_bus & rd_data_valid_mask;
rd_valid_o <= |rd_valid_bus;
end else begin
rd_data_o <= {40{1'b0}};
rd_valid_o <= 1'b0;
end
end
end
作者:IC小鸽
文章来源:IC的世界
推荐阅读
更多IC设计干货请关注IC设计专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。