Verilog功能模块——串行数据转并行数据

一. 模块功能与应用场景

模块功能:接收串行数据,转为并行数据。

应用场景:在SPI,Uart等串行协议接收侧均有应用。


二. 模块框图与使用说明

Verilog功能模块-串行数据转并行数据-1

有两种模式(通过参数SDATA_IS_CONTINUOUS进行选择):

1.数据连续模式,此时sdata_valid指示有效数据开始,在并行数据接收完成前,后面数据均有效。输入时序如下图。

Verilog功能模块-串行数据转并行数据-2

2.数据不连续模式,此时sdata_valid指示当前数据有效。输入时序如下图。

Verilog功能模块-串行数据转并行数据-3

注意:

1.sdata与sdata_valid应同步有效,且因为代码中有同步处理,所以这两者不需要与sclk的某边沿同步

2.sdata应从最高位开始发

2.第一组数据和第二组数据之间的间隔可以为0~N个时钟周期,即可以连续发不间隔,也可以有任意间隔。


三. 模块代码

/*
 * @Author       : Xu Dakang
 * @Email        : XudaKang_up@qq.com
 * @Date         : 2021-04-24 12:27:11
 * @LastEditors  : Xu Dakang
 * @LastEditTime : 2021-04-25 21:08:14
 * @Filename     : sdata2pdata.sv
 * @Description  : 输入串行数据,输出并行数据,实现串转并
*/



module sdata2pdata
#(
  parameter PDATA_WIDTH = 24,
  parameter SDATA_IS_CONTINUOUS = 0
)(
  output logic [PDATA_WIDTH-1 : 0] pdata,
  output logic                     pdata_valid,

  input  logic                     sdata,
  input  logic                     sdata_valid,

  input  logic                     sclk,

  input  logic rstn
);



//< 输入信号同步 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
logic sdata_r1;
logic sdata_r2;
logic sdata_r3;
always_ff @(posedge sclk) begin
  sdata_r1 <= sdata;
  sdata_r2 <= sdata_r1;
  sdata_r3 <= sdata_r2;
end


logic sdata_valid_r1;
logic sdata_valid_r2;
always_ff @(posedge sclk) begin
  sdata_valid_r1 <= sdata_valid;
  sdata_valid_r2 <= sdata_valid_r1;
end
//< 输入信号同步 ------------------------------------------------------------



//> 串行数据计数 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
logic [$clog2(PDATA_WIDTH)-1 : 0] sdata_cnt;

logic sdata_cnt_en;
always_ff @(posedge sclk, negedge rstn) begin
  if (~rstn)
    sdata_cnt_en <= '0;
  else if (SDATA_IS_CONTINUOUS)
    if (sdata_valid_r2 && (sdata_cnt == '0 || sdata_cnt == PDATA_WIDTH - 1 || sdata_cnt ==  PDATA_WIDTH))
      sdata_cnt_en <= 1'b1;
    else if (~sdata_valid_r2 && sdata_cnt ==  PDATA_WIDTH - 1)
      sdata_cnt_en <= 1'b0;
    else
      sdata_cnt_en <= sdata_cnt_en;
  else
    sdata_cnt_en <= sdata_valid_r2;
end


always_ff @(posedge sclk, negedge rstn) begin
  if (~rstn)
    sdata_cnt <= '0;
  else if (sdata_cnt_en)
    if (sdata_cnt == PDATA_WIDTH) // 数据有效时,一组数据刚转换完成,下一组的第一个数据来了,计为1
      sdata_cnt <= 'b1;
    else //! 数据有效时,一组数据还未转换完成,计数加1
      sdata_cnt <= sdata_cnt + 1'b1;
  else if (sdata_cnt == PDATA_WIDTH) // 一组数据转换完成,下一组数据没马上来,回到0
    sdata_cnt <= '0;
  else
    sdata_cnt <= sdata_cnt;
end
//> 串行数据计数 ------------------------------------------------------------



//< 生成输出 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
logic [PDATA_WIDTH-1 : 0] pdata_temp; // pdata的计算中间值
always_ff @(posedge sclk, negedge rstn) begin
  if (~rstn)
    pdata_temp <= '0;
  else if (sdata_cnt_en)
    pdata_temp <= {pdata_temp[PDATA_WIDTH-2 : 0], sdata_r3};
  else
    pdata_temp <= pdata_temp;
end


always_ff @(posedge sclk, negedge rstn) begin
  if (~rstn)
    pdata <= '0;
  else if (sdata_cnt == PDATA_WIDTH)
    pdata <= pdata_temp;
  else
    pdata <= pdata;
end


always_ff @(posedge sclk, negedge rstn) begin
  if (~rstn)
    pdata_valid <= '0;
  else if (sdata_cnt == PDATA_WIDTH)
    pdata_valid <= 1'b1;
  else
    pdata_valid <= '0;
end
//< 生成输出 ------------------------------------------------------------



endmodule

四. testbench

/*
 * @Author       : Xu Dakang
 * @Email        : XudaKang_up@qq.com
 * @Date         : 2021-04-24 12:27:28
 * @LastEditors  : Xu Dakang
 * @LastEditTime : 2021-04-25 21:05:02
 * @Filename     : sdata2pdata_tb.sv
 * @Description  : testbench of sdata2pdata
*/



module sdata2pdata_tb ();

timeunit 1ns;
timeprecision 10ps;

localparam PDATA_WIDTH = 5;
localparam SDATA_IS_CONTINUOUS = 1;

logic [PDATA_WIDTH-1 : 0] pdata;
logic                     pdata_valid;

logic                     sdata;
logic                     sdata_valid;
logic                     sclk;
logic                     rstn;



// 实例化模块
sdata2pdata #(
  .PDATA_WIDTH (PDATA_WIDTH),
  .SDATA_IS_CONTINUOUS (SDATA_IS_CONTINUOUS)
) sdata2pdata_inst(.*);



// 产生测试数据 最大值 2^PDATA_WIDTH-1
localparam NUM = 15;
logic [PDATA_WIDTH-1 : 0] pdata_list [NUM];
initial begin
  for (int i = 0; i < NUM; i++) begin
    pdata_list[i] = {$random()} % (2**PDATA_WIDTH);
  end
end



// 生成时钟
localparam CLKT = 2;
initial begin
  sclk = 0;
  forever #(CLKT / 2) sclk = ~sclk;
end



// 数据连续模式
initial begin
  if (SDATA_IS_CONTINUOUS == 1) begin
    rstn = 0;
    sdata_valid = 0;
    #(CLKT * 2)  rstn = 1;
    for (int i = 0; i < NUM; i++) begin
      sdata_valid = 1;
      for (int j = 0; j < PDATA_WIDTH; j++) begin
        sdata = pdata_list[i][PDATA_WIDTH-1-j];
        #(CLKT) sdata_valid = 0;
      end
      #(CLKT * ({$random} % 3)) ; // 数据连续模式时,可在各组数据间插入随机时钟间隔
    end

    #(CLKT * 10) $stop;
  end
end


// 数据不连续模式
initial begin
  if (SDATA_IS_CONTINUOUS == 0) begin
    rstn = 0;
    sdata_valid = 0;
    #(CLKT * 2)  rstn = 1;
    for (int i = 0; i < NUM; i++) begin
      for (int j = 0; j < PDATA_WIDTH; j++) begin
        sdata = pdata_list[i][PDATA_WIDTH-1-j];
        sdata_valid = 1;
        #CLKT ;
        sdata_valid = 0;
        #(CLKT * ({$random} % 3)) ; // 数据不连续模式时,可在同组数据间插入随机时钟间隔
      end
      sdata_valid = 0;
      #(CLKT * ({$random} % 3)) ;
    end

    #(CLKT * 10) $stop;
  end
end



endmodule

五. 仿真验证

仿真工具:Vivado 2020.2 Simulator。

数据连续模式,从结果可以看出,串转并输出正确。

Verilog功能模块-串行数据转并行数据--4

数据不连续模式,同样正确。

Verilog功能模块-串行数据转并行数据-5

六. 工程分享

sdata2pdata 串转并模块 vivado 2020.2工程.7z

欢迎大家关注我的公众号:徐晓康的博客,回复以下代码获取。

5689

建议复制过去不会码错字!


徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。

  • 8
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
下面是一个简单的8位并行-串行自动换电路的Verilog HDL代码: ```verilog module ParallelToSerial( input clk, // 时钟信号 input reset, // 复位信号 input [7:0] parallel_data, // 并行数据输入 input enable, // 使能信号 output reg serial_out // 串行数据输出 ); reg [7:0] shift_reg; // 移位寄存器 always @(posedge clk) begin if (reset) begin shift_reg <= 8'b0; // 在复位信号下,清除移位寄存器 end else if (enable) begin shift_reg <= {shift_reg[6:0], parallel_data[7]}; // 将并行数据输入移位到移位寄存器中 end end always @(posedge clk) begin if (reset) begin serial_out <= 1'b0; // 在复位信号下,将串行数据输出置为0 end else if (enable) begin serial_out <= shift_reg[7]; // 将移位寄存器中的最高位作为串行数据输出 end end endmodule ``` 该代码定义了一个名为ParallelToSerial的模块,其中包含四个输入信号(时钟信号、复位信号、并行数据输入和使能信号)和一个输出信号(串行数据输出)。该模块使用一个移位寄存器来完成从并行数据输入到串行数据输出的自动换。 在时钟信号的上升沿触发的always块中,如果接收到复位信号,移位寄存器将被清零。否则,如果接收到使能信号,移位寄存器将把输入的最高位添加到其末尾。 在另一个时钟信号的上升沿触发的always块中,如果接收到复位信号,串行数据输出将被置零。否则,如果接收到使能信号,串行数据输出将设置为移位寄存器中的最高位。 这样,当使能信号被设置为高电平时,模块就会自动从并行数据输入中读取数据,将其换为串行数据输出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值