概述
一. 模块功能与应用场景
模块功能:接收串行数据,转为并行数据。
应用场景:在SPI,Uart等串行协议接收侧均有应用。
二. 模块框图与使用说明
有两种模式(通过参数SDATA_IS_CONTINUOUS进行选择):
1.数据连续模式,此时sdata_valid指示有效数据开始,在并行数据接收完成前,后面数据均有效。输入时序如下图。
2.数据不连续模式,此时sdata_valid指示当前数据有效。输入时序如下图。
注意:
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。
数据连续模式,从结果可以看出,串转并输出正确。
数据不连续模式,同样正确。
六. 工程分享
sdata2pdata 串转并模块 vivado 2020.2工程.7z
欢迎大家关注我的公众号:徐晓康的博客,回复以下代码获取。
5689
建议复制过去不会码错字!
徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。
最后
以上就是活力夕阳为你收集整理的Verilog功能模块——串行数据转并行数据的全部内容,希望文章能够帮你解决Verilog功能模块——串行数据转并行数据所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复