前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >串并转换(串入并出、并入串出、移位寄存器法和计数器法|verilog代码|Testbench|仿真结果)

串并转换(串入并出、并入串出、移位寄存器法和计数器法|verilog代码|Testbench|仿真结果)

原创
作者头像
Loudrs
修改2023-05-18 16:49:44
5.5K0
修改2023-05-18 16:49:44
举报


数字IC经典电路设计

经典电路设计是数字IC设计里基础中的基础,盖大房子的第一部是打造结实可靠的地基,每一篇笔者都会分门别类给出设计原理、设计方法、verilog代码、Testbench、仿真波形。然而实际的数字IC设计过程中考虑的问题远多于此,通过本系列希望大家对数字IC中一些经典电路的设计有初步入门了解。能力有限,纰漏难免,欢迎大家交流指正。快速导航链接如下:

个人主页链接

1.数字分频器设计

2.序列检测器设计

3.序列发生器设计

4.序列模三检测器设计

5.奇偶校验器设计

6.自然二进制数与格雷码转换

7.线性反馈移位寄存器LFSR

8.四类九种移位寄存器总结

9.串并转换



一、前言

数字电路中的串并转换主要设计思想来源于用面积换速度,对数据流进行管理。实现串并转换的主要方式有双口RAM,FIFO,移位寄存器等,对于数据量较大的一般使用双口RAM或者FIFO实现,数据量较小的使用移位寄存器实现。

在设计的时候主要包括以下两个模块

  • 核心模块:对于移位寄存器法,每个时钟周期将1bit数据缓存在寄存器上,选择不同的转换优先方式数据缓存的方式不同。选择msb优先的情况下,数据将会从高位向低位依次缓存,相当于数据不断地右移,lsb优先则相反。对于计数器法,相比于移位寄存器逐次移位进行拼接,计数器法通过计数器将输入的数据直接缓存到对应的位次。
  • 辅助模块:由于串并行数据输入输出的特性,输入的时钟周期与输出时钟周期是不同的。以8-1串并串入并出转换为例,每个时钟周期输入1bit数据,在8个时钟周期后数据全部并行输出。此时,有两种方法可以选择:一是借用使能信号,二是借用分频器。前者简单但是灵活性稍差,此处采用后者进行设计。此处详细设计可以参考数字分频器设计

二、串行输入并行输出转换

2.1 移位寄存器实现串行输入并行输出

2.11移位寄存器实现串行输入并行输出

根据移位寄存器的原理,每个时钟周期将1bit数据缓存在寄存器上,对于选择msb优先和lsb优先:

dout_msb_r <= {dout_msb_rWIDTH - 2 :0,din_r}; dout_lsb_r <= {din_r,dout_lsb_rWIDTH - 1 :1};

然后在8分频时钟信号下由时序逻辑输出。

2.12 verilog代码

代码语言:c
复制
//1-8串并转换器
module sipo_sr #(
    parameter WIDTH = 8   
    )(
    input    clk,
    input    rst_n,
    input    din,
    output   reg [WIDTH - 1 : 0] dout_msb,//高位优先输出
    output   reg [WIDTH - 1 : 0] dout_lsb//低位优先输出
    );


//计数器实现的8分频模块(开始
//计数器
reg [3:0] cnt;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        cnt <= 4'b0 ;
    end
    else if (cnt == 3) begin		
        cnt <= 4'b0 ;
    end
    else begin				
        cnt <= cnt + 1'b1 ;
    end
end

//信号翻转生成8分频信号
reg clk_div8_r;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        clk_div8_r <= 1'b0;
    end
    else if (cnt == 3 ) begin		
        clk_div8_r <= ~clk_div8_r;
    end
end
    
assign clk_div8 = clk_div8_r;
//计数器的8分频模块(结束

//移位寄存器模块(开始
//输入先寄存一拍
reg din_r;
always @(posedge clk or negedge rst_n) 
    if(!rst_n) begin
	    din_r <= 1'b0;
    end
    else begin
	    din_r <= din;
    end
    
//最高位优先输出
reg [WIDTH - 1 : 0] dout_msb_r; 
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    dout_msb_r <= 0;
    end
    else begin
	    dout_msb_r <= {dout_msb_r[WIDTH - 2 :0],din_r};
    end
end

//最低位优先输出
reg [WIDTH - 1 : 0] dout_lsb_r;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    dout_lsb_r <= 0;
    end
    else begin
	    dout_lsb_r <= {din_r,dout_lsb_r[WIDTH - 1 :1]};
    end
end
//移位寄存器模块(结束

//时序逻辑输出
always@(posedge clk_div8 or negedge rst_n) begin
    if(!rst_n) begin
        dout_msb <= 0;
        dout_lsb <= 0;
    end
    else begin
        dout_msb  <= dout_msb_r;
        dout_lsb  <= dout_lsb_r;
    end
end

endmodule

2.13 Testbench

代码语言:c
复制
`timescale 1ns/1ps	//仿真时间单位1ns 仿真时间精度1ps
module sipo_sr_tb #(
    parameter WIDTH = 8
    );

///信号申明
reg      clk;
reg      rst_n;
reg      din;
wire     [WIDTH - 1 : 0]    dout_msb;
wire     [WIDTH - 1 : 0]    dout_lsb;

//模块实例化(将申明的信号连接起来即可)
siposr u_sipo_sr(
    .clk			(clk),
    .rst_n			(rst_n),
    .din			(din),
    .dout_msb		(dout_msb),
    .dout_lsb		(dout_lsb)
    );

always #5 clk = ~clk;	//生成时钟信号

//调用系统命令——监视
initial begin
      $monitor ("rst_n = %b, din = %b, dout_msb = %b, dout_lsb = %b", 
       		rst_n, din, dout_msb, dout_lsb);
end

//为输入数据赋值
initial begin
    clk   = 0;
    rst_n = 1;
    din   = 1'b0;
    #5  rst_n = 0;
    #5  rst_n = 1;
        din =1'b1;
    #10 din =1'b0;
    #10 din =1'b1;
    #10 din =1'b0;
    #10 din =1'b1;
    #10 din =1'b0;
    #10 din =1'b0;
    #10 din =1'b1;
    #10 din =1'b1;
    #200 $finsh;
end

endmodule 

2.14 仿真结果

2.2计数器实现串行输入并行输出

2.21移位寄存器实现串行输入并行输出

根据计数器法的原理,每个时钟周期将1bit数据缓存在寄存器上,对于选择msb优先和lsb优先:

dout_msb_rWIDTH - 1 - cnt_sipo <= din_r; dout_lsb_rcnt_sipo <= din_r;

然后在8分频时钟信号下由时序逻辑输出。

2.22 verilog代码

代码语言:c
复制
//1-8串并转换器
module sipo_cnt #(
    parameter WIDTH = 8   
    )(
    input    clk,
    input    rst_n,
    input    din,
    input    up_edge,
    output   reg [WIDTH - 1 : 0] dout_msb,//高位优先输出
    output   reg [WIDTH - 1 : 0] dout_lsb//低位优先输出
    );


//计数器实现的8分频模块(开始
//计数器
reg [3:0] cnt;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        cnt <= 4'b0 ;
    end
    else if (cnt == 3) begin		
        cnt <= 4'b0 ;
    end
    else begin				
        cnt <= cnt + 1'b1 ;
    end
end

//信号翻转生成8分频信号
reg clk_div8_r;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        clk_div8_r <= 1'b0;
    end
    else if (cnt == 3 ) begin		
        clk_div8_r <= ~clk_div8_r;
    end
end
 
reg clk_div8;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        clk_div8 <= 1'b0;
    end
    else begin		
        clk_div8 <= clk_div8_r;
    end
end
 
//计数器的8分频模块(结束

assign up_edge   = !clk_div8 & clk_div8_r;

//输入先寄存一拍
reg din_r;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    din_r <= 1'b0;
    end
    else begin
	    din_r <= din;
    end
end

//串并工作计数器
reg [2 : 0] cnt_sipo;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    cnt_sipo <= 1'b0;
    end
    else if(up_edge)begin
	    cnt_sipo <= 1'b0;
    end
    else begin
	    cnt_sipo <= cnt_sipo + 1'b1;
    end
end

    
//最高位优先输出
reg [WIDTH - 1 : 0] dout_msb_r; 
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    dout_msb_r <= 0;
    end
    else begin
	    dout_msb_r[WIDTH - 1 - cnt_sipo] <= din_r;
    end
end

//最低位优先输出
reg [WIDTH - 1 : 0] dout_lsb_r;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    dout_lsb_r <= 0;
    end
    else begin
	    dout_lsb_r[cnt_sipo] <= din_r;
    end
end

//时序逻辑输出(延迟一拍输出)
always@(posedge clk_div8 or negedge rst_n) begin
    if(!rst_n) begin
        dout_msb <= 0;
        dout_lsb <= 0;
    end
    else begin
        dout_msb  <= dout_msb_r;
        dout_lsb  <= dout_lsb_r;
    end
end

endmodule

2.23 Testbench

代码语言:c
复制
`timescale 1ns/1ps	//仿真时间单位1ns 仿真时间精度1ps
module sipo_cnt_tb #(
    parameter WIDTH = 8
    );

///信号申明
reg      clk;
reg      rst_n;
reg      din;
wire     [WIDTH - 1 : 0]    dout_msb;
wire     [WIDTH - 1 : 0]    dout_lsb;

//模块实例化(将申明的信号连接起来即可)
sipo_cnt u_sipo_cnt(
    .clk			(clk),
    .rst_n			(rst_n),
    .din			(din),
    .dout_msb		(dout_msb),
    .dout_lsb		(dout_lsb)
    );

always #5 clk = ~clk;	//生成时钟信号

//调用系统命令——监视
initial begin
      $monitor ("rst_n = %b, din = %b, dout_msb = %b, dout_lsb = %b", 
       		rst_n, din, dout_msb, dout_lsb);
end

//为输入数据赋值
initial begin
    clk   = 0;
    rst_n = 1;
    din   = 1'b0;
    #5  rst_n = 0;
    #5  rst_n = 1;
        din =1'b1;
    #10 din =1'b0;
    #10 din =1'b1;
    #10 din =1'b0;
    #10 din =1'b1;
    #10 din =1'b0;
    #10 din =1'b0;
    #10 din =1'b1;
    #10 din =1'b1;
    #200 $finsh;
end

endmodule 

2.24 仿真结果

三、并行输入串行输出转换

3.1 移位寄存器实现串行输入并行输出

3.1.1移位寄存器实现并行输入串行输出

根据移位寄存器的原理,每8个时钟周期输入一组8bit数据,在8分频时钟的上升端(借助上升沿检测)缓存在寄存器中:

else if(up_edge) begin dout_lsb_r <= din_r; end else if(up_edge) begin dout_msb_r <= din_r; end

然后每个时钟周期将1bit数据输出,对于选择msb优先和lsb优先:

dout_msb_rWIDTH - 1 - cnt_sipo <= din_r; dout_lsb_rcnt_sipo <= din_r;

然后在8分频时钟信号下由组合逻辑输出。

3.1.2 verilog代码

代码语言:c
复制
//8-1串并转换器
module sr_piso #(
    parameter WIDTH = 8   
    )(
    input    clk,
    input    rst_n,
    input    [WIDTH - 1 : 0] din,
    output   up_edge,
    output   dout_msb,//高位优先输出
    output   dout_lsb//低位优先输出
    );


//计数器实现的8分频模块(开始
//计数器
reg [3:0] cnt;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        cnt <= 4'b0 ;
    end
    else if (cnt == 3) begin		
        cnt <= 4'b0 ;
    end
    else begin				
        cnt <= cnt + 1'b1 ;
    end
end

//信号翻转生成8分频信号
reg clk_div8_r;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        clk_div8_r <= 1'b0;
    end
    else if (cnt == 3 ) begin		
        clk_div8_r <= ~clk_div8_r;
    end
end

//8分频信号上升沿检测——先寄存一拍
reg clk_div8;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    clk_div8 <= 1'b0;
    end
    else begin
	    clk_div8<= clk_div8_r;
    end
end

assign up_edge   = !clk_div8 & clk_div8_r;//8分频信号上升沿检测——逻辑运算输出
//计数器的8分频模块(结束


//移位寄存器模块(开始
//输入先寄存一拍
reg [WIDTH - 1 : 0] din_r;
always @(posedge clk_div8 or negedge rst_n) 
    if(!rst_n) begin
	    din_r <= 0;
    end
    else begin
	    din_r <= din;
    end

//最高位优先输出
reg [WIDTH - 1 : 0] dout_msb_r; 
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    dout_msb_r <= 0;
    end
    else if(up_edge) begin
	    dout_msb_r <= din_r;
    end
    else begin
	    dout_msb_r <= {dout_msb_r[WIDTH - 2 :0],1'b0};
    end
end

//最低位优先输出
reg [WIDTH - 1 : 0] dout_lsb_r;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	   dout_lsb_r <= 0;
    end
    else if(up_edge) begin
	     dout_lsb_r <= din_r;
    end
    else begin
	    dout_lsb_r <= {1'b0,dout_lsb_r[WIDTH - 1 :1]};
    end
end
//移位寄存器模块(结束

//组合逻辑输出
assign dout_msb  = dout_msb_r[WIDTH - 1];
assign dout_lsb  = dout_lsb_r[0];
 
endmodule

3.1.3 Testbench

代码语言:c
复制
`timescale 1ns/1ps	//仿真时间单位1ns 仿真时间精度1ps
module sr_piso_tb #(
    parameter WIDTH = 8
    );

//信号申明
reg      clk;
reg      rst_n;
reg      [WIDTH - 1 : 0]din;
wire     dout_msb;
wire     dout_lsb;

//模块实例化(将申明的信号连接起来即可)
sr_piso u_sr_piso(
    .clk		(clk),
    .rst_n		(rst_n),
    .din		(din),
    .dout_msb		(dout_msb),
    .dout_lsb		(dout_lsb)
    );

always #5 clk = ~clk;	//生成时钟信号

//调用系统命令——监视
initial begin
      $monitor ("rst_n = %b, din = %b, dout_msb = %b, dout_lsb = %b", 
       		rst_n, din, dout_msb, dout_lsb);
end

//为输入数据赋值
initial begin
    clk   = 0;
    rst_n = 1;
    din   = 8'b0;
    #5  rst_n = 0;
    #5  rst_n = 1;
        din =8'b10011010;
    #100 din =8'b10101010;
    #100 din =8'b11111111;
    #200 $finsh;
end

endmodule 

3.1.4 仿真结果

3.2计数器实现并行输入串行输出

3.2.1计数器实现并行输入串行输出

根据移位寄存器的原理,每8个时钟周期输入一组8bit数据,在8分频时钟的上升端(借助上升沿检测)缓存在寄存器中:

else begin dout_msb_r <= dout_msb_r; dout_lsb_r <= dout_lsb_r; end

然后将1bit数据输出,对于选择msb优先和lsb优先:

assign dout_msb = dout_msb_rWIDTH - 1 - cnt_piso; assign dout_lsb = dout_lsb_rcnt_piso;

然后在8分频时钟信号下由组合逻辑输出。

3.2.2 verilog代码

代码语言:c
复制
//8-1串并转换器
module piso_cnt #(
    parameter WIDTH = 8   
    )(
    input    clk,
    input    rst_n,
    input    [WIDTH - 1 : 0] din,
    output   up_edge,
    output   dout_msb,//高位优先输出
    output   dout_lsb//低位优先输出
    );


//计数器实现的8分频模块(开始
//计数器
reg [3:0] cnt;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        cnt <= 4'b0 ;
    end
    else if (cnt == 3) begin		
        cnt <= 4'b0 ;
    end
    else begin				
        cnt <= cnt + 1'b1 ;
    end
end

//信号翻转生成8分频信号
reg clk_div8_r;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin		
        clk_div8_r <= 1'b0;
    end
    else if (cnt == 3 ) begin		
        clk_div8_r <= ~clk_div8_r;
    end
end

//8分频信号上升沿检测——先寄存一拍
reg clk_div8;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    clk_div8 <= 1'b0;
    end
    else begin
	    clk_div8<= clk_div8_r;
    end
end

assign up_edge   = !clk_div8 & clk_div8_r;//8分频信号上升沿检测——逻辑运算输出
//计数器的8分频模块(结束


//输入先寄存一拍
reg [WIDTH - 1 : 0] din_r;
always @(posedge clk_div8 or negedge rst_n) 
    if(!rst_n) begin
	    din_r <= 0;
    end
    else begin
	    din_r <= din;
    end

//并串工作计数器
reg [2 : 0] cnt_piso;
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
	    cnt_piso <= 1'b0;
    end
    else if(up_edge)begin
	    cnt_piso <= 1'b0;
    end
    else begin
	    cnt_piso <= cnt_piso + 1'b1;
    end
end

//将输入在同一时钟缓存到寄存器中
reg [WIDTH - 1 : 0] dout_msb_r;
reg [WIDTH - 1 : 0] dout_lsb_r;
always @(posedge clk or negedge rst_n) 
    if(!rst_n) begin
	    dout_msb_r <= 0;
	    dout_lsb_r <= 0;
    end
    else if(up_edge)begin
	    dout_msb_r <= din_r;
	    dout_lsb_r <= din_r;
    end
    else begin
	    dout_msb_r <= dout_msb_r;
	    dout_lsb_r <= dout_lsb_r;
    end

//组合逻辑即使输出
assign dout_msb = dout_msb_r[WIDTH - 1 - cnt_piso];
assign dout_lsb = dout_lsb_r[cnt_piso];

 
endmodule

3.2.3 Testbench

代码语言:c
复制
`timescale 1ns/1ps	//仿真时间单位1ns 仿真时间精度1ps
module piso_cnt_tb #(
    parameter WIDTH = 8
    );

//信号申明
reg      clk;
reg      rst_n;
reg      [WIDTH - 1 : 0]din;
wire     dout_msb;
wire     dout_lsb;

//模块实例化(将申明的信号连接起来即可)
piso_cnt u_piso_cnt(
    .clk		(clk),
    .rst_n		(rst_n),
    .din		(din),
    .dout_msb		(dout_msb),
    .dout_lsb		(dout_lsb)
    );

always #5 clk = ~clk;	//生成时钟信号

//调用系统命令——监视
initial begin
      $monitor ("rst_n = %b, din = %b, dout_msb = %b, dout_lsb = %b", 
       		rst_n, din, dout_msb, dout_lsb);
end

//为输入数据赋值
initial begin
    clk   = 0;
    rst_n = 1;
    din   = 8'b0;
    #5  rst_n = 0;
    #5  rst_n = 1;
        din =8'b10011010;
    #100 din =8'b10101010;
    #100 din =8'b11111111;
    #200 $finsh;
end

endmodule 

3.2.4 仿真结果

四、总结

  • 串入并出(移位寄存器法):根据移位寄存器的原理,每个时钟周期将1bit数据缓存在寄存器上,对于选择msb优先和lsb优先:

 dout_msb_r <= {dout_msb_rWIDTH - 2 :0,din_r};  dout_lsb_r <= {din_r,dout_lsb_rWIDTH - 1 :1};

然后在8分频时钟信号下由时序逻辑输出

  • 串入并出(计数器法): 根据计数器法的原理,每个时钟周期将1bit数据缓存在寄存器上,工作计数器在复位或者8分频上升端(借用上升沿检测)清零,对于选择msb优先和lsb优先:

 dout_msb_rWIDTH - 1 - cnt_sipo <= din_r;  dout_lsb_rcnt_sipo <= din_r;

然后在8分频时钟信号下由时序逻辑输出。

  • 并入串出(移位寄存器法): 根据移位寄存器的原理,每8个时钟周期输入一组8bit数据,在8分频时钟的上升端(借助上升沿检测)缓存在寄存器中:

     else if(up_edge) begin          dout_lsb_r <= din_r;      end      else if(up_edge) begin          dout_msb_r <= din_r;      end

然后每个时钟周期将1bit数据输出,对于选择msb优先和lsb优先:

 dout_msb_rWIDTH - 1 - cnt_sipo <= din_r;  dout_lsb_rcnt_sipo <= din_r;

然后在8分频时钟信号下由组合逻辑输出。

  • 并入串出(计数器法): 根据移位寄存器的原理,每8个时钟周期输入一组8bit数据,在8分频时钟的上升端(借助上升沿检测)缓存在寄存器中:

    else begin        dout_msb_r <= dout_msb_r;        dout_lsb_r <= dout_lsb_r;     end

 然后将1bit数据输出,对于选择msb优先和lsb优先:

 assign dout_msb = dout_msb_rWIDTH - 1 - cnt_piso;  assign dout_lsb = dout_lsb_rcnt_piso;

然后在8分频时钟信号下由组合逻辑输出。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、串行输入并行输出转换
    • 2.1 移位寄存器实现串行输入并行输出
      • 2.11移位寄存器实现串行输入并行输出
      • 2.12 verilog代码
      • 2.13 Testbench
      • 2.14 仿真结果
    • 2.2计数器实现串行输入并行输出
      • 2.21移位寄存器实现串行输入并行输出
      • 2.22 verilog代码
      • 2.23 Testbench
      • 2.24 仿真结果
  • 三、并行输入串行输出转换
    • 3.1 移位寄存器实现串行输入并行输出
      • 3.1.1移位寄存器实现并行输入串行输出
      • 3.1.2 verilog代码
      • 3.1.3 Testbench
      • 3.1.4 仿真结果
    • 3.2计数器实现并行输入串行输出
      • 3.2.1计数器实现并行输入串行输出
      • 3.2.2 verilog代码
      • 3.2.3 Testbench
      • 3.2.4 仿真结果
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档