除了基本模式和包模式外,在上下游模块数据位宽不一致的情况下,FIFO还常常承接位宽转换的作用。
对于位宽转换常常采用多个小位宽数据通过拼接成一个大位宽数据的方式。特别的,为了简化设计,大位宽数据的位宽应该是小位宽数据位宽的整倍数。(当遇到不是整倍数的情况,可以采用多个位宽转换串接的方式)
接口定义
对于位宽转换模块的接口定义如下,
axis_dwidth #(
parameter M_TDATA_WIDTH = 4,
parameter S_TDATA_WIDTH = 2
) (
input logic clk,
input logic [M_TDATA_WIDTH - 1 : 0] m_axis_tdata,
input logic m_axis_tvalid, // wr_en
output logic m_axis_tready, // ~wfull
output logic [S_TDATA_WIDTH - 1 : 0] s_axis_tdata,
output logic s_axis_tvalid, // ~rempty
input logic s_axis_tready // rd_en
);
位宽检查
为了防止用户定义的数据位宽不满足上文整数倍的要求,需要首先对数据位宽进行判断。
localparam integer M_DIV = M_TDATA_WIDTH / S_TDATA_WIDTH;
localparam integer S_DIV = S_TDATA_WIDTH / M_TDATA_WIDTH;
localparam integer M_DIV_sub1 = M_DIV - 1;
localparam integer S_DIV_sub1 = S_DIV - 1;
integer err_flag = 0;
initial begin : check_err
err_flag = 0;
if (M_TDATA_WIDTH == S_TDATA_WIDTH) begin
$error("位宽相同,不需要转换");
err_flag = 1;
end
if (M_DIV * S_TDATA_WIDTH == M_TDATA_WIDTH) ;
else if (S_DIV * M_TDATA_WIDTH == S_TDATA_WIDTH) ;
else begin
$error("不是整数倍");
err_flag = 1;
end
if (err_flag) begin
$stop();
end
end // : check_err
位宽转换
根据计算出的M_DIV和S_DIV,若M_DIV<S_DIV得出是上游过来的数据向下游数据位宽对齐,若S_DIV>M_DIV则反之。以S_DIV>M_DIV的情况为例,利用位扩展方式,位宽转换过程如下:
if (S_DIV != 0) begin
localparam DEPTH_WIDTH = $clog2(S_DIV) + 1; // int($clog(3)) <= $clog2(3) == $clog2(4)
logic [S_TDATA_WIDTH - 1 : 0] ram_tdata;
logic [0 : 0] rd_cnt_r = 'd0;
logic [DEPTH_WIDTH : 0] wr_cnt_r = 'd0;
always_ff @(posedge clk) begin
if (m_axis_tvalid & m_axis_tready) begin
if (wr_cnt_r[DEPTH_WIDTH - 1 : 0] == S_DIV_sub1) begin
wr_cnt_r[DEPTH_WIDTH - 1 : 0] <= 'd0;
wr_cnt_r[DEPTH_WIDTH] <= ~wr_cnt_r[DEPTH_WIDTH];
end else begin
wr_cnt_r[DEPTH_WIDTH - 1 : 0] <= wr_cnt_r[DEPTH_WIDTH - 1 : 0] + 'd1;
end
end
end
always_ff @(posedge clk) begin
if (s_axis_tready & s_axis_tvalid) begin
rd_cnt_r <= ~rd_cnt_r;
end
end
always_ff @(posedge clk) begin
if (m_axis_tvalid & m_axis_tready) begin
ram_tdata[wr_cnt_r[DEPTH_WIDTH - 1 : 0] * M_TDATA_WIDTH +: M_TDATA_WIDTH] <= m_axis_tdata;
end
end
always_comb begin
s_axis_tdata = ram_tdata;
end
assign m_axis_tready_s = (wr_cnt_r[DEPTH_WIDTH] == rd_cnt_r[0]);
assign s_axis_tvalid_s = (wr_cnt_r[DEPTH_WIDTH] != rd_cnt_r[0]);
assign s_axis_tvalid = s_axis_tvalid_s;
assign m_axis_tready = m_axis_tready_s;
end
仿真结果如下:
M_DIV>S_DIV的情况与其类似,这里仅给出仿真结果。
完整代码
本文所涉及的完整工程可于公众号回复AXIS_AWIDTH下载。