Verilog位宽转换器参数化设计

        摘要:Verilog 位宽转换 参数化设计

1、应用场景

        Verilog位宽转换应用场景很多,比如ADC数据采集存储,CameraLink数据采集存储等很多需要进行位宽转换的地方都需要用到。换句话说,只要输入输出位宽不匹配,就会用到。常规的位宽转换方法有FIFO IP、以及移位寄存器等。但如果输入输出位宽之间不是整数倍的关系又该如何处理?大概都会想到用移位寄存器+计数器+数据选择器实现。那么有没有一种通用写法呢?这里说的通用写法也就是可不可以实现参数化。

2、工程实现

        我想到的一种实现方式是参考FIFO的思想,如果有人有其他更好的方法欢迎交流学习。和一般的FIFO只有一维指针不同,这里用到了二维指针。大致示意图如下:

07dce88d25684a11a3522cbe2cafe6fb.png

        模块代码:

// 时间:2023-03-17
// 作者:xiaoxingdlx(1957158385@qq.com)
// 模块:位宽转换示例
module unit_wide_convert #(
    parameter simTdly      = 1,  // 仿真延时参数
    parameter dinWidth     = 4,  // 输入数据位宽
    parameter doutWidth    = 8,  // 输出数据位宽
    parameter bufWidth     = 16, // 缓存数据位宽(输入和输出位宽最小公倍数的整数倍)
    parameter bufDeepth    = 1,  // 缓存数据深度(2^N)
    parameter ptrWidthW    = 2,  // 写指针位宽(内部指针,根据缓存数据位宽/输入数据位宽-1为最大值计算)
    parameter ptrWidthR    = 1   // 读指针位宽(内部指针,根据缓存数据位宽/输出数据位宽-1为最大值计算)
) (
    input  wire                                 clk,      // 时钟
    input  wire                                 reset,    // 复位,高有效
    input  wire  [dinWidth-1:0]                 din,      // 数据输入
    input  wire                                 din_vld,  // 数据输入valid信号
    output wire                                 din_rdy,  // 数据输入ready信号
    output wire  [doutWidth-1:0]                dout,     // 数据输出
    output wire                                 dout_vld, // 数据输出valid信号
    input  wire                                 dout_rdy  // 数据输出ready信号
);
    // 获取二维指针内部指针的最大计数值
    localparam WR_PTR_1_MAX = bufWidth/dinWidth  - 'd1;
    localparam RD_PTR_1_MAX = bufWidth/doutWidth - 'd1;
    // 数据缓存
    reg   [bufWidth-1:0]                        buffer [2**bufDeepth-1:0];
    // 二维写指针
    reg   [bufDeepth:0]                         wr_ptr_0; // 外部指针
    reg   [ptrWidthW-1:0]                       wr_ptr_1; // 内部指针
    // 二维读指针
    reg   [bufDeepth:0]                         rd_ptr_0; // 外部指针
    reg   [ptrWidthR-1:0]                       rd_ptr_1; // 内部指针
    // 二维写指针控制逻辑
    always @(posedge clk ) begin
        if (reset) begin
            wr_ptr_0 <= #simTdly 'd0;
            wr_ptr_1 <= #simTdly 'd0;
        end else begin
            if (din_vld && din_rdy && wr_ptr_1 == WR_PTR_1_MAX) begin
                wr_ptr_0 <= #simTdly wr_ptr_0 + 'd1;
            end
            if (din_vld && din_rdy && wr_ptr_1 == WR_PTR_1_MAX) begin
                wr_ptr_1 <= #simTdly 'd0;
            end else if (din_vld && din_rdy) begin
                wr_ptr_1 <= #simTdly wr_ptr_1 + 'd1;
            end
        end
    end
    // 二维读指针控制逻辑
    always @(posedge clk ) begin
        if (reset) begin
            rd_ptr_0 <= #simTdly 'd0;
            rd_ptr_1 <= #simTdly 'd0;
        end else begin
            if (dout_vld && dout_rdy && rd_ptr_1 == RD_PTR_1_MAX) begin
                rd_ptr_0 <= #simTdly rd_ptr_0 + 'd1;
            end
            if (dout_vld && dout_rdy && rd_ptr_1 == RD_PTR_1_MAX) begin
                rd_ptr_1 <= #simTdly 'd0;
            end else if (dout_vld && dout_rdy) begin
                rd_ptr_1 <= #simTdly rd_ptr_1 + 'd1;
            end
        end
    end
    // 数据缓存写入读出
    always @(posedge clk ) begin
        if (din_vld && din_rdy) begin
            buffer[wr_ptr_0[0 +: bufDeepth]] <= #simTdly (buffer[wr_ptr_0[0 +: bufDeepth]] << dinWidth) | din;
        end
        if (dout_vld && dout_rdy) begin
            buffer[rd_ptr_0[0 +: bufDeepth]] <= #simTdly (buffer[rd_ptr_0[0 +: bufDeepth]] << doutWidth);
        end
    end
    // 握手信号
    assign din_rdy = (wr_ptr_0[bufDeepth] != rd_ptr_0[bufDeepth] && wr_ptr_0[0 +:bufDeepth] == rd_ptr_0[0 +:bufDeepth]) ?  1'b0 : 1'b1;
    assign dout_vld = (wr_ptr_0 == rd_ptr_0) ? 1'b0 : 1'b1;
    // 数据输出
    assign dout = buffer[rd_ptr_0[0 +:bufDeepth]][(bufWidth-doutWidth) +: doutWidth];

    //
    /*
    unit_wide_convert #(
        .simTdly   (simTdly   ),
        .dinWidth  (dinWidth  ),
        .doutWidth (doutWidth ),
        .bufWidth  (bufWidth  ),
        .bufDeepth (bufDeepth ),
        .ptrWidthW (ptrWidthW ),
        .ptrWidthR (ptrWidthR )
    )
    u_unit_wide_convert(
    	.clk      (clk      ),
        .reset    (reset    ),
        .din      (din      ),
        .din_vld  (din_vld  ),
        .din_rdy  (din_rdy  ),
        .dout     (dout     ),
        .dout_vld (dout_vld ),
        .dout_rdy (dout_rdy )
    );
    */

endmodule

        仿真模块:

//~ `New testbench
`timescale  1ns / 1ps

module tb_unit_wide_convert;

// unit_wide_convert Parameters
parameter PERIOD     = 10;
parameter simTdly    = 1;  // 仿真延时参数
parameter dinWidth   = 4;  // 输入数据位宽
parameter doutWidth  = 8;  // 输出数据位宽
parameter bufWidth   = 16; // 缓存数据位宽(输入和输出位宽最小公倍数的整数倍)
parameter bufDeepth  = 1;  // 缓存数据深度(2^N)
parameter ptrWidthW  = 2;  // 写指针位宽(内部指针,根据缓存数据位宽/输入数据位宽-1为最大值计算)
parameter ptrWidthR  = 1;  // 读指针位宽(内部指针,根据缓存数据位宽/输出数据位宽-1为最大值计算)

// unit_wide_convert Inputs
reg   clk                                  = 1 ;
reg   reset                                = 1 ;
reg   [dinWidth-1:0]  din                  = 0 ;
reg   din_vld                              = 1 ;
reg   dout_rdy                             = 1 ;

// unit_wide_convert Outputs
wire  din_rdy                              ;
wire  [doutWidth-1:0]  dout                ;
wire  dout_vld                             ;


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2+1) reset  =  0;
end

always @(posedge clk ) begin
    if (reset) begin
        din <= #simTdly 'd0;
    end else if (din_vld && din_rdy) begin
        din <= #simTdly din + 'd1;
    end
end

unit_wide_convert #(
    .simTdly   (simTdly   ),
    .dinWidth  (dinWidth  ),
    .doutWidth (doutWidth ),
    .bufWidth  (bufWidth  ),
    .bufDeepth (bufDeepth ),
    .ptrWidthW (ptrWidthW ),
    .ptrWidthR (ptrWidthR )
)
u_unit_wide_convert(
    .clk      (clk      ),
    .reset    (reset    ),
    .din      (din      ),
    .din_vld  (din_vld  ),
    .din_rdy  (din_rdy  ),
    .dout     (dout     ),
    .dout_vld (dout_vld ),
    .dout_rdy (dout_rdy )
);

endmodule

        仿真波形:

f45fd3feb3824a488f442aa9b88f2ad3.png

        仿真模块:

//~ `New testbench
`timescale  1ns / 1ps

module tb_unit_wide_convert;

// unit_wide_convert Parameters
// parameter PERIOD     = 10;
// parameter simTdly    = 1;  // 仿真延时参数
// parameter dinWidth   = 4;  // 输入数据位宽
// parameter doutWidth  = 8;  // 输出数据位宽
// parameter bufWidth   = 16; // 缓存数据位宽(输入和输出位宽最小公倍数的整数倍)
// parameter bufDeepth  = 1;  // 缓存数据深度(2^N)
// parameter ptrWidthW  = 2;  // 写指针位宽(内部指针,根据缓存数据位宽/输入数据位宽-1为最大值计算)
// parameter ptrWidthR  = 1;  // 读指针位宽(内部指针,根据缓存数据位宽/输出数据位宽-1为最大值计算)

parameter PERIOD     = 10;
parameter simTdly    = 1;  // 仿真延时参数
parameter dinWidth   = 5;  // 输入数据位宽
parameter doutWidth  = 8;  // 输出数据位宽
parameter bufWidth   = 40; // 缓存数据位宽(输入和输出位宽最小公倍数的整数倍)
parameter bufDeepth  = 1;  // 缓存数据深度(2^N)
parameter ptrWidthW  = 3;  // 写指针位宽(内部指针,根据缓存数据位宽/输入数据位宽-1为最大值计算)
parameter ptrWidthR  = 3;  // 读指针位宽(内部指针,根据缓存数据位宽/输出数据位宽-1为最大值计算)

// unit_wide_convert Inputs
reg   clk                                  = 1 ;
reg   reset                                = 1 ;
reg   [dinWidth-1:0]  din                  = 0 ;
reg   din_vld                              = 1 ;
reg   dout_rdy                             = 1 ;

// unit_wide_convert Outputs
wire  din_rdy                              ;
wire  [doutWidth-1:0]  dout                ;
wire  dout_vld                             ;


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2+1) reset  =  0;
end

always @(posedge clk ) begin
    if (reset) begin
        din <= #simTdly 'd0;
    end else if (din_vld && din_rdy) begin
        din <= #simTdly din + 'd1;
    end
end

unit_wide_convert #(
    .simTdly   (simTdly   ),
    .dinWidth  (dinWidth  ),
    .doutWidth (doutWidth ),
    .bufWidth  (bufWidth  ),
    .bufDeepth (bufDeepth ),
    .ptrWidthW (ptrWidthW ),
    .ptrWidthR (ptrWidthR )
)
u_unit_wide_convert(
    .clk      (clk      ),
    .reset    (reset    ),
    .din      (din      ),
    .din_vld  (din_vld  ),
    .din_rdy  (din_rdy  ),
    .dout     (dout     ),
    .dout_vld (dout_vld ),
    .dout_rdy (dout_rdy )
);

endmodule

        仿真波形:

8210c2a8c6074e11a08bff403420faf9.png         上面标注的是0,1,2,3,4,5,6,7(后面忘标了)

d144ce94b5f1462b9577a358f045c570.png        上面标注的是8,9,10,11,12,13,14,15,16... 

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值