写在前面的话
串并/并串转换是FPGA设计过程中的一个重要技巧,经常被用在高速数据流处理中,特别是在通信接口方面尤为重要。如UART串口协议、SPI串口协议、IIC串口协议都需要用到串并/并串转换。本节,梦翼师兄和大家一起来学习串并/并串转换的实现方法。
需求分析
并转串的设计思想是这样的:首先准备好一组寄存器,把需要发送的数据(并行数据)放到这个寄存器组里面,然后通过位拼接的移位方式把这个并行数据一位一位地发送给接收端,同时拉高标志信号en,当全部数据发送完之后,在把标志信号en拉低。
顶层框架设计
顶层模块端口介绍
端口名 |
端口说明 |
clk |
系统50MHz时钟输入 |
rst_n |
系统低电平复位 |
en |
数据转换标志信号 |
sda |
输出的串行数据 |
代码实现
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function : 并转串模块 *****************************************************/ 01 module para_serial( 02 //系统输入 03 clk, //50M系统时钟输入 04 rst_n, //系统复位 05 //系统输出 06 sda, //串行数据总线 07 en //数据转换标志信号 08 ); 09 //-------------系统输入-------------- 10 input clk; //50M系统时钟输入 11 input rst_n;//系统复位 12 //-------------系统输出-------------- 13 output reg sda; //串行数据总线 14 output reg en; //数据转换标志信号 15 //------------寄存器定义------------- 16 reg [7:0]sda_buf; //并行数据寄存器 17 reg [3:0]counter; //移位计数器 18 //-----------并串转换逻辑------------ 19 always@(posedge clk or negedge rst_n) 20 begin 21 if(!rst_n) 22 begin 23 sda<=0; //串行数据总线赋初值 24 sda_buf<=8'b1001_1101; //并行数据寄存器赋初值 25 counter<=0; //移位计数器赋初值 26 en<=0; //数据转换标志信号赋初值 27 end 28 else 29 begin 30 if(counter<8)//控制移位次数 31 begin 32 en<=1; //转换使能打开 33 counter<=counter+1'b1; //通过位拼接方式实现并串转换 34 sda_buf<={sda_buf[6:0],sda_buf[7]}; 35 sda<=sda_buf[7]; //将sda_buf一位位的发送出去 36 end 37 else 38 begin 39 counter<=0;//移位结束,计数器清零 40 sda<=0; 41 en<=0; //转换完成 42 end 43 end 44 end 45 46 endmodule |
代码分析:我们首先定义了一个8位的数据寄存器sda_buf和一个控制移位的计数器counter,然后在电路复位的时候给sda_buf写入了一个8位的并行数据8’b1001_1101;第30~35行,我们拉高数据转换标志信号en,把并行数据sda_buf的最高位输出到sda,同时sda_buf进行左移操作,sda_buf中的数据就会以串行的方式一位一位的发送出去。第39~41行,移位结束的同时,计数器清零,数据转换标志信号en变为低电平,就完成了一次串行数据向并行数据的转换。
编写的测试代码如下:
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function : 并转串测试模块 *****************************************************/ 01 `timescale 1ns/1ps 02 module tb; 03 04 //------------被测试模块输入------------ 05 reg clk; //50M系统时钟输入 06 reg rst_n; //系统复位 07 //------------被测试模块输出------------- 08 wire sda; //串行数据总线 09 wire en; //数据转换标志信号 10 //------------设置测试激励--------------- 11 initial 12 begin 13 clk=0; 14 rst_n=0; 15 # 1000.1 rst_n=1; 16 end 17 18 always #10 clk=~clk; //周期为20ns的时钟 19 20 //-------------模块实例化---------------- 21 para_serial para_serial ( 22 .clk(clk), 23 .rst_n(rst_n), 24 .sda(sda), 25 .en(en) 26 ); 27 endmodule |
仿真波形如下所示:
观察波形,可以看到当数据转换标志信号en为高电平的时候,sda_buf中的数据从最高位开始输出,也就是sda开始输出串行数据,当sda_buf中的全部数据输出后的同时,数据转换标志信号en变为低电平。
接下来,我们来学习串行数据转换成并行数据的设计实现技巧,由于我们已经有一个并转串的模块来输出串行数据,所以我们可以巧妙的利用这个模块。
我们在测试代码中加入一个串行数据转换成并行数据的模块,它的部件架构图如下(下面测试代码第24~41行):
修改后的测试代码如下:
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function :串转并测试模块 *****************************************************/ 01 `timescale 1ns/1ns 02 module tb; 03 04 //------------被测试模块输入------------ 05 reg clk; //50M系统时钟输入 06 reg rst_n; //系统复位 07 //------------被测试模块输出------------- 08 wire sda; //串行数据总线 09 wire en; //数据转换标志信号 10 //------------设置测试激励--------------- 11 initial 12 begin 13 clk=0; 14 rst_n=0; 15 # 1000 rst_n=1; 16 end 17 18 always #10 clk=~clk; //周期为20ns的时钟 19 20 //------------串并转换模块--------------- 21 reg [7:0]sda_reg; //串行数据接收寄存器 22 wire en; 23 24 always@(posedge clk or negedge rst_n) 25 begin 26 if(!rst_n) 27 begin 28 sda_reg<=0; 29 end 30 else 31 begin 32 if(en) //检测到数据转换标志信号为高电平 33 begin //将输入的串行数据sda转换为并行数据sda_reg 34 sda_reg<={sda_reg[6:0],sda}; 35 end 36 else //检测到en为低电平,不采取任何操作 37 begin 38 sda_reg<=0; 39 end 40 end 41 end 42 //-------------模块实例化---------- 43 para_serial para_serial ( 44 .clk(clk), 45 .rst_n(rst_n), 46 .sda(sda), 47 .en(en) 48 ); 49 endmodule |
代码分析:我们首先定义了一个串行数据接收寄存器sda_reg,检测到数据转换标志信号en有效时,sda_reg通过位拼接的方式依次将并转串模块输出的串行数据sda移入到sda_reg的每一位(从低位到高位),从而实现串行数据向并行数据的转换。
仿真波形如下所示:
观察波形,可以看到当数据转换标志信号en为高电平的时候,sda_reg开始接收串行数据sda,当数据转换标志信号en为低电平的时,串行数据向并行数据的转换刚好结束,我们可以看出转换得到的并行数据sda_reg和发送出的并行数据sda_buf是完全相同的,说明我们的设计是正确的。