赞
踩
数字IC经典电路设计
经典电路设计是数字IC设计里基础中的基础,盖大房子的第一部是打造结实可靠的地基,每一篇笔者都会分门别类给出设计原理、设计方法、verilog代码、Testbench、仿真波形。然而实际的数字IC设计过程中考虑的问题远多于此,通过本系列希望大家对数字IC中一些经典电路的设计有初步入门了解。能力有限,纰漏难免,欢迎大家交流指正。
快速导航链接如下:
什么是移位寄存器?
组成:由具有存储功能的触发器构成。另外,寄存器还应有执行数据接收和清除命令的控制电路,一般由门电路构成。
功能:移位寄存器可寄存一组二值代码,N个触发器组成的寄存器可以存储一组N位的二值代码,一般用于将二进制数据从一个位置转移到另一个位置
移位寄存器有哪些分类呢?
(1)右移移位寄存器
(2)左移移位寄存器
(3)双向移位寄存器
为便于扩展逻辑功能和增加使用的灵活性,在单向移位寄存器基础上,增加由门电路组成的控制电路,便可构成双向移位寄存器。目前,在定型生产的中规模移位寄存器集成电路上除了附加左、右移控制,一般还附有数据并行输入、保持、异步置零(复位)等功能。
一般双向移位寄存器逻辑图示例:
要求:设计一个四位循环移位寄存器,包括三种移位寄存器,分别是左移移位寄存器、右移移位寄存器、双向移位寄存器。
//三个四位宽的循环移位寄存器 //左移移位寄存器、右移移位寄存器、双向移位寄存器 module sr_simple #( parameter WIDTH = 4 //定义数据位宽 )( input clk, input rst_n, input [1:0] model, //双向移位寄存器输出模式选择 input [WIDTH - 1 : 0] data_in, output [WIDTH - 1 : 0] data_left, //左移移位寄存器输出 output [WIDTH - 1 : 0] data_right, //右移移位寄存器输出 output [WIDTH - 1 : 0] data_bidi //双向移位寄存器输出 ); //定义双向移位寄存器三种状态——保持、左移、右移 parameter keep = 2'b00; parameter left_shift = 2'b01; parameter right_shift = 2'b10; //左移移位寄存器工作模块 //借助拼接符实现移位 reg [WIDTH - 1 : 0] data_left_r; //中间寄存器 always@(posedge clk or negedge rst_n) begin if(!rst_n) begin data_left_r <= data_in; end else begin data_left_r <= {data_left_r[WIDTH - 2 : 0], data_left_r[WIDTH - 1]}; end end //右移移位寄存器工作模块 //借助拼接符实现移位 reg [WIDTH - 1 : 0] data_right_r; //中间寄存器 always@(posedge clk or negedge rst_n) begin if(!rst_n) begin data_right_r <= data_in; end else begin data_right_r <= {data_right_r[0],data_right_r[WIDTH - 1 : 1]}; end end //双向移位寄存器工作模块 //借助拼接符实现移位,case语句实现状态选择 reg [WIDTH - 1 : 0] data_bidi_r; //中间寄存器 always@(posedge clk or negedge rst_n) begin if(!rst_n) begin data_bidi_r <= data_in; end else begin case(model) keep: data_bidi_r <= data_bidi_r; left_shift: data_bidi_r <= {data_bidi_r[WIDTH - 2 : 0], data_bidi_r[WIDTH - 1]}; right_shift: data_bidi_r <= {data_bidi_r[0],data_bidi_r[WIDTH - 1 : 1]}; default: data_bidi_r <= data_bidi_r; endcase end end //组合逻辑输出 assign data_left = data_left_r; assign data_right = data_right_r; assign data_bidi = data_bidi_r; endmodule
`timescale 1ns/1ps //仿真时间单位1ns 仿真时间精度1ps module sr_simple_tb #( parameter WIDTH = 4 //定义数据位宽 ); //信号申明 reg clk; reg rst_n; reg [1:0] model; reg [WIDTH - 1 : 0] data_in; wire [WIDTH - 1 : 0] data_left; wire [WIDTH - 1 : 0] data_right; wire [WIDTH - 1 : 0] data_bidi; //模块实例化(将申明的信号连接起来即可) sr_simple u_sr_simple( .clk (clk), .rst_n (rst_n), .model (model), .data_in (data_in), .data_left (data_left), .data_right (data_right), .data_bidi (data_bidi) ); always #5 clk = ~clk; //生成时钟信号 //调用系统命令——监视 initial begin $monitor ("rst_n = %b, data_in = %b, data_left = %b, data_right = %b, model = %b,data_bidi = %b", rst_n, data_in, data_left, data_right,model,data_bidi); end //为输入数据赋值 initial begin clk = 0; rst_n = 1; data_in = 4'b1001; model = 2'b00; #5 rst_n = 0; #5 rst_n = 1; #40 model = 2'b01; #40 model = 2'b00; #40 model = 2'b10; #100 $finsh; end endmodule
逻辑移位与算术移位寄存器均属于非循环移位寄存器;
逻辑移位:逻辑移位是指逻辑左移和逻辑右移,移出的空位都用0来补。
算术移位:算术移位就需要分有符号型值和无符号型值。对于无符号型值,算术移位等同于逻辑移位;而对于有符号型值,算术左移等同于逻辑左移,算术右移补的是符号位,正数补0,负数补1。
简而言之,两者的区别在于:逻辑移位不考虑符号位,左移和右移都只补零;算术移位考虑符号位,左移补零,右移补符号位。
对于二进制的数值来说右移n位等于原来的数值除以2的n次方
Tips:这种倍数关系只适用于右移后被舍弃的低位不含1的情况,否则每舍一次1则代表余数被舍去,保留整数部分。比如10110100十进制是76(需要先将这个补码转换成原码之后再转换成十进制),右移两位后是11101101转成十进制是19恰好是76的4倍。但是101十进制是5,逻辑右移一位是010十进制是2,此时舍掉了余数1;
要求:设计一个四位非循环移位寄存器,可实现逻辑右边移和算术右移(逻辑左移和算术左移相同,此处不做展开)。
//四位宽非循环移位寄存器 //可实现逻辑右边移和算术右移 module sr_log_ari #( parameter WIDTH = 4 //定义数据位宽 )( input clk, input rst_n, input [WIDTH - 1:0] data_in, output [WIDTH - 1:0] data_log, //逻辑右移输出 output [WIDTH - 1:0] data_ari //算术右移输出 ); //逻辑移位工作模块 reg [WIDTH -1:0] data_log_r;//中间寄存器 always@(posedge clk or posedge rst_n)begin if(!rst_n) begin data_log_r <= data_in; end else begin data_log_r <= {1'b0,data_log_r[WIDTH - 1:1]}; end end //算术移位工作模块 reg [WIDTH -1:0] data_ari_r;//中间寄存器 always@(posedge clk or posedge rst_n)begin if(!rst_n) begin data_ari_r <= data_in; end else if(!data_log_r[WIDTH - 1]) begin data_ari_r <= {1'b0,data_ari_r[WIDTH - 1:1]};//最高位是“1”则补“1” end else begin data_ari_r <= {1'b1,data_ari_r[WIDTH - 1:1]};//最高位是“0”则补“0” end end //组合逻辑输出 assign data_log = data_log_r; assign data_ari = data_ari_r; endmodule
`timescale 1ns/1ps //仿真时间单位1ns 仿真时间精度1ps module sr_log_ari_tb #( parameter WIDTH = 4 //定义数据位宽 ); ///信号申明 reg clk; reg rst_n; reg [WIDTH - 1 : 0] data_in; wire [WIDTH - 1 : 0] data_log; wire [WIDTH - 1 : 0] data_ari; //模块实例化(将申明的信号连接起来即可) sr_log_ari u_sr_log_ari( .clk (clk), .rst_n (rst_n), .data_in (data_in), .data_log (data_log), .data_ari (data_ari) ); always #5 clk = ~clk;//生成时钟信号 //调用系统命令——监视 initial begin $monitor ("rst_n = %b, data_in = %b, data_log = %b, data_ari = %b", rst_n, data_in, data_log, data_ari); end //为输入数据赋值 initial begin clk = 0; rst_n = 1; data_in = 4'b1001; #5 rst_n = 0; #5 rst_n = 1; #40; data_in =4'b0110; #5 rst_n = 0; #5 rst_n = 1; #40 $finsh; end endmodule
根据存放数码的方式不同分为并行和串行两种:并行方式就是将寄存的数码从各对应的输入端同时输入到寄存器中;串行方式是将数码从一个输入端逐位输入到寄存器中。根据取出数码的方式不同也可分为并行和串行两种:并行方式就是要取出的数码从对应的各个输出端上同时出现;串行方式是被取出的数码在一个输出端逐位输出;
例如:需要传输的数据有32bit,用串行传输则需要32个时钟周期完成传输,如果用8位并行传输,则32bit数据只需要4个时钟周期就可以完成传输。
根据以上数据输入输出分类,此时有四种移位寄存器:①串入串出②串入并出③并入串出④并入并出。下文将重点对串-并和并串移位寄存器进行分析,并且给出相应verilog代码、Testbench和仿真。
转换可以采用两种方式实现
- msb优先(Most Significant Bit):最高比特,即最高位优先
- lsb优先(Least Significant Bit):最低比特,即最低位优先
串-并移位寄存器拥有多个存储单元,每个存储单元都可以存储一个二进制数位。当输入一串二进制数据时,时钟信号会依次将每个数据位移入到寄存器的最低位。当所有数据位都被移入后,就可以通过并行输出将数据同时输出到多个接收器上。
要求:实现一个串并转换器,每个时钟周期输入1bit数据,8个时钟周期后数据并行输出,分别采用MSB优先和LSB优先输出。
//1-8串并转换器 module sr_sipo #( 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 sr_sipo_tb #( parameter WIDTH = 8 ); ///信号申明 reg clk; reg rst_n; reg din; wire [WIDTH - 1 : 0] dout_msb; wire [WIDTH - 1 : 0] dout_lsb; //模块实例化(将申明的信号连接起来即可) sr_sipo u_sr_sipo( .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
并转串电路主要由时钟(clk)、复位信号(rst_n)、并行输入信号、串行输出信号和使能信号组成。使能信号表示开始执行并转串操作,由于并转串是移位操作,先将八位数据暂存于一个八位寄存器器中,然后左移输出到一位输出端口,通过一个“移位”来实现,当一次并转串完成后,需要重新载入待转换的并行数据时,使能信号要再起来一次。
要求:实现一个并串转换器,每个时钟周期输出1bit数据,8个时钟周期后数据全部输出,分别采用MSB优先和LSB优先输出。
//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
此处更详细可以参考线性反馈移位寄存器LFSR(斐波那契LFSR(多到一型)和伽罗瓦LFSR(一到多型)|verilog代码|Testbench|仿真结果)
斐波那契LFSR为多到一型LFSR,即多个触发器的输出经过异或逻辑来驱动一个触发器的输入。反馈多项式为
f
(
x
)
=
x
3
+
x
2
+
1
f(x)=x^3 + x^2 +1
f(x)=x3+x2+1,即
x
1
x_1
x1的输入为
x
3
x_3
x3和
x
2
x_2
x2的输出异或后的结果,电路图如下所示:
输出序列的顺序为:111-110-100-001-010-101-011-111
//三级斐波那契LFSR设计 //反馈多项式为 f(x)=x^3 + x^2 +1 module lfsr_fibonacci( input clk, input rst_n, output reg [2:0] q ); //时序逻辑LFSR移位模块 always @(posedge clk or rst_n) begin if (!rst_n) begin q <= 3'b111; //种子值为111 end else begin q <= {q[1],q[0],q[1]^q[2]}; //根据三级斐波那契LFSR电路拼接输出 end end endmodule
`timescale 1ns/1ps //仿真时间单位1ns 仿真时间精度1ps module lfsr_fibonacci_tb(); //信号申明 reg clk; reg rst_n; wire [2:0] q; //模块实例化(将申明的信号连接起来即可) lfsr_fibonacci u_lfsr_fibonacci( .clk (clk), .rst_n (rst_n), .q (q) ); always #5 clk = ~clk; //生成时钟信号 //为输入数据赋值 initial begin clk = 1; rst_n = 1; #5 rst_n = 0; #5 rst_n = 1; #1000 $stop; end endmodule
伽罗瓦LFSR为一到多型LFSR,即一个触发器的输出经过异或逻辑来驱动多个触发器的输入。对于同样的反馈多项式
x
3
+
x
2
+
1
x^3+x^2+1
x3+x2+1而言:触发器
x
1
x_1
x1的输入通常来源于触发器
x
2
x_2
x2的输出,
x
3
x_3
x3(最高项)的输入通常来自于
x
1
x_1
x1的输出,此多项式中剩余触发器的输入是
x
1
x1
x1的输出与前级输出异或的结果,
x
2
x_2
x2的输入由
x
1
x_1
x1的输出与
x
3
x_3
x3的输出通过异或运算得到。其电路图如下所示:
输出序列的顺序为:111-101-100-010-001-110-011-111
//三级伽罗瓦LFSR设计 //反馈多项式为 f(x)=x^3 + x^2 +1 module lfsr_galois( input clk, input rst_n, output reg [2:0] q ); //时序逻辑LFSR移位模块 always @(posedge clk or rst_n) begin if (!rst_n) begin q <= 3'b111; //种子值为111 end else begin q <= {q[0],q[2]^q[0],q[1]}; //根据三级伽罗瓦LFSR电路拼接输出 end end endmodule
`timescale 1ns/1ps //仿真时间单位1ns 仿真时间精度1ps module lfsr_galois_tb(); //信号申明 reg clk; reg rst_n; wire [2:0] q; //模块实例化(将申明的信号连接起来即可) lfsr_galois u_lfsr_galois( .clk (clk), .rst_n (rst_n), .q (q) ); always #5 clk = ~clk; //时钟信号生成 //为输入数据赋值 initial begin clk = 1; rst_n = 1; #5 rst_n = 0; #5 rst_n = 1; #1000 $stop; end endmodule
简单循环左移/右移/双向移位寄存器:设计简单,主要通过verilog语法中的拼接运算符“{}”完成,双向移位寄存器的设计在左、右移位寄存器的基础上通过case语句(if也行、三目运算符亦可),整体设计偏简单,可应用在序列检测器与序列发生器中。
逻辑移位与算术移位寄存器:最重要的是弄清楚逻辑移位与算术移位的原理、逻辑移位与算术移位的区别(左移一致,仅右移有区别),在此基础上根据上一个简单移位寄存器进行设计。
Tips:逻辑移位与算术移位寄存器是非循环移位寄存器,拼接的时候采用“0”“1”拼接。
串-并移位寄存器与并-串移位寄存器:此处需要强调一点,就是在设计的串并转换时一定要考虑串并数据的时钟周。所以设计时包含以下几个模块:数字分频器和移位寄存器(数字分频器参考数字分频。初此以外,在设计并行的时候还引入了8分频的上升沿检测(当时在这卡壳了很久,或许还有其他更好地方法)。
- 线性反馈移位寄存器LFSR:简易的LFSR包括种子可抽头的设计,种子通过赋初值实现,抽头设计只要通过拼接运算符和XOR实现。此处更详细可以参考线性反馈移位寄存器LFSR(斐波那契LFSR(多到一型)和伽罗瓦LFSR(一到多型)|verilog代码|Testbench|仿真结果)LFSR广泛应用于伪随机数生成、伪噪声序列生成、计数器、数据的加密和CRC校验、扰码器/解码器、信号生成和测试等领域,是一种非常有用的数字电路设计技术。
不定期检查、补充、纠错,欢迎随时交流纠错
最后修改日期:2023.5.15
软件版本:
仿真软件:Modelsim 10.6c
绘图软件:亿图图示
描述语言:verilog
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。