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

请在此添加图片描述



数字IC经典电路设计

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

个人主页链接

1.数字分频器设计

2.序列检测器设计

3.序列发生器设计

4.序列模三检测器设计

5.奇偶校验器设计

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

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

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



一、前言

数字电路中的串并转换主要设计思想来源于用面积换速度,对数据流进行管理。实现串并转换的主要方式有双口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代码

//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

`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代码

//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

`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代码

//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

`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代码

//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

`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分频时钟信号下由组合逻辑输出。


更多可查看个人主页链接
软件版本:仿真软件:Modelsim 10.6c
不定期纠错补充,欢迎随时交流
最后修改日期:2023.6.2

  • 8
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值