数字IC经典电路设计
经典电路设计是数字IC设计里基础中的基础,盖大房子的第一部是打造结实可靠的地基,每一篇笔者都会分门别类给出设计原理、设计方法、verilog代码、Testbench、仿真波形。然而实际的数字IC设计过程中考虑的问题远多于此,通过本系列希望大家对数字IC中一些经典电路的设计有初步入门了解。能力有限,纰漏难免,欢迎大家交流指正。快速导航链接如下:
个人主页链接
1.数字分频器设计
2.序列检测器设计
3.序列发生器设计
4.序列模三检测器设计
5.奇偶校验器设计
6.自然二进制数与格雷码转换
7.线性反馈移位寄存器LFSR
8.四类九种移位寄存器总结
数字电路中的串并转换主要设计思想来源于用面积换速度,对数据流进行管理。实现串并转换的主要方式有双口RAM,FIFO,移位寄存器等,对于数据量较大的一般使用双口RAM或者FIFO实现,数据量较小的使用移位寄存器实现。
在设计的时候主要包括以下两个模块
根据移位寄存器的原理,每个时钟周期将1bit数据缓存在寄存器上,对于选择msb优先和lsb优先:
dout_msb_r <= {dout_msb_rWIDTH - 2 :0,din_r};
dout_lsb_r <= {din_r,dout_lsb_rWIDTH - 1 :1};
然后在8分频时钟信号下由时序逻辑输出。
//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
`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
根据计数器法的原理,每个时钟周期将1bit数据缓存在寄存器上,对于选择msb优先和lsb优先:
dout_msb_rWIDTH - 1 - cnt_sipo <= din_r;
dout_lsb_rcnt_sipo <= din_r;
然后在8分频时钟信号下由时序逻辑输出。
//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
`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
根据移位寄存器的原理,每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-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
`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
根据移位寄存器的原理,每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分频时钟信号下由组合逻辑输出。
//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
`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
串入并出(移位寄存器法): 根据移位寄存器的原理,每个时钟周期将1bit数据缓存在寄存器上,对于选择msb优先和lsb优先:
dout_msb_r <= {dout_msb_rWIDTH - 2 :0,din_ r};
dout_lsb_r <= {din_r,dout_lsb_rWIDTH - 1 :1};
然后在8分频时钟信号下由时序逻辑输出
dout_msb_rWIDTH - 1 - cnt_sipo <= din_r;
dout_lsb_rcnt_sipo <= din_r;
然后在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分频时钟信号下由组合逻辑输出。
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