?早期的硬盘为IDE并行接口,如今的电子设备多以串行接口接收信号,如温度传感器DS18B20为串行总线、采用串行接口AD采样芯片会极大节省CPU的I/O口。那具体的串行数据是如何被读入CPU内部呢?
答案就是通过移位寄存器实现!
?移位寄存器是一种时序逻辑电路,能够存储和传输数据。它们由触发器组成,这些触发器的连接方式使得一个触发器的输出可以作为另一个触发器的输入,具体取决于所创建的移位寄存器的类型。移位寄存器基本上是一种能够传输(“移位”)数据的寄存器。寄存器通常是存储设备,它们是通过将特定数量的触发器串联在一起而创建的,并且寄存器可以存储的数据量(位数)始终与触发器的数量成正比,因为每个触发器一次只能存储一个bit。当寄存器中的触发器以这样的方式连接时,一个触发器的输出成为另一个触发器的输入,就会创建一个移位寄存器。
?触发器是具有类似于闩锁操作的设备,被称为双稳态电路,有两个稳定的状态,即0或1,并且能够以bit为单位存储数据。每当D触发器的时钟发生变化时(上升沿或下降沿,取决于触发器的规格)。输出“Q”处的数据与输入“D”处的数据相同。触发器的输出“Q”将保持在该值,直到下一个时钟周期,然后它将再次更改为输入处的值(1或0)。
有关触发器的具体介绍请进入传送门: 【IC设计】时序逻辑的基础—锁存器、触发器
?移位寄存器主要根据其工作模式(串行或并行)分为几类。下面列出了几种基本移位寄存器,尽管其中一些可以根据数据流的方向进一步划分,右移或者左移。
传送门:[PDF]移位寄存器及其应用 - 中国科学技术大学
- 串口输入—串口移位寄存器 (SISO)
- 串行输入—并行输出移位寄存器 (SIPO)
- 并联输入—并联输出移位寄存器 (PIPO)
- 并联输入—串行移位寄存器 (PISO)
- 双向移位寄存器
如果在赋值语句中需要左右移位,尽量不要用语法中提供的左移“<<”和右移“>>”符号
因为不同的综合工具对他们的处理方式不同,导致逻辑综合的结果不可控,即使代码通过了功能仿真和FPGA验证,也有可能在ASIC实现时出问题。最稳妥的移位方法要把每位的移动具体写出来.
例如,向右循环移位可写为(a[6:0]<=a[7:1];a[7]<=a[0]),由于这种写法落实到具体的位,所以逻辑综合的结果将不依赖于EDA工具,这样命运就会掌握在自己的手里,不用去关心EDA工具如何处理左右移位。
------ 引用自《CMOS模拟集成电路全流程设计》 李金城 机械工业出版社
构建一个100位的左右旋转器,同步load,左右旋转需使能。旋转器从另一端输入移位的位元,不像移位器那样丢弃移位的位元而以零位移位。如果启用,旋转器就会旋转这些位,而不会修改或丢弃它们。
load:加载100位的移位寄存器数据
ena[1:0]:2’b01 右转1bit; 2’b10 左转1bit;其他情况不转
q:旋转器内容
module top_module(
input clk,
input load,
input [1:0] ena,
input [99:0] data,
output reg [99:0] q);
always@(posedge clk)begin
if(load == 1'b1)begin
q <= data;
end else begin
/* if(ena == 2'b01)begin
q <= {q[0], q[99:1]};
end
if(ena == 2'b10)begin
q <= {q[98:0], q[99]};
end
if(ena == 2'b00 && ena == 2'b11)begin
q <= q;
end
end
*/
case(ena)
2'b01: q <= {q[0], q[99:1]};
2'b10: q <= {q[98:0], q[99]};
default: q <= q;
endcase
end
end
endmodule
4位双向移位寄存器真值表
建立一个64位算术移位寄存器,同步加载。移位器可以左右移位,并按数量选择1位或8位的移位。
1、算术右移将移位移位寄存器中数字(在本例中为q[63])的符号位,而不是逻辑右移所做的零。
2、另一种考虑算术右移的方法是它假设被移的数是有符号的并保留符号,所以算术右移可以将一个有符号的数除以2的幂。
load:加载数据
ena:决定是否移位
amount:决定移位方向与数量:2’b00:左移1位;2’b01:左移8位;2’b10:右移1位;2’b11:右移8位
q:寄存器内容(输出)
module top_module(
input clk,
input load,
input ena,
input [1:0] amount,
input [63:0] data,
output reg [63:0] q);
always@(posedge clk)begin
if(load == 1'b1)begin
q <= data;
end else begin
if(ena == 1'b1)begin
case(amount)
2'b00: //shift left by 1 bit
q <= {q[62:0], 1'b0};
2'b01: //shift left by 8 bits
q <= {q[55:0], 8'b0};
2'b10: //shift right by 1 bit
q <= {q[63], q[63:1]};
2'b11: //shift right by 8 bits
q <= {{8{q[63]}}, q[63:8]};
//default:
endcase
end else begin
q <= q;
end
end
end
endmodule
线性反馈移位寄存器(LFSR)主要包括:
1、其中,gn为反馈系数,取值只能为0或1,取为0时表明不存在该反馈之路,取为1时表明存在该反馈之路;
2、这里的反馈系数决定了产生随机数的算法的不同。
3、用反馈函数表示成y = a0x^0 + a1x + a2x^2…
4、反馈函数为线性的叫线性移位反馈序列,否则叫非线性反馈移位序列。
5、Q1、Q2、Q3、Qn为LFSR的输出,M(x)是输入的码字多项式,如M(x)=x^4+ x^1+ 1,表示输入端的输入顺序为11001,同样,LFSR的结构也可以表示为多项式G(x),称为生成多项式:G(x) = gn * x^n+ …+g1 * x^1+ g0;
练习:
构造线性移位寄存器,reset应当使LFSR归1。
module top_module(
input clk,
input reset, // Active-high synchronous reset to 5'h1
output [4:0] q
);
always@(posedge clk)begin
if(reset == 1'b1)begin
q <= 5'h1;
end else begin
q[4] <= q[0] ^ 1'b0;
q[3] <= q[4];
q[2] <= q[3] ^ q[0];
q[1] <= q[2];
q[0] <= q[1];
end
end
endmodule
同样,异名例化,用计数器的方法定义时钟
为这个序列电路编写Verilog代码。假设你要在DE1-SoC板上实现这个电路。将R输入连接到SW开关,将时钟连接到密钥[0],将L连接到密钥[1],将Q输出连接到红灯LEDR上。
注:该电路是线性反馈移位寄存器(LFSR) 的一个示例。最大周期 LFSR 可用于生成伪随机数,因为它在重复之前循环 2 n -1 个组合。全零组合不会出现在此序列中。
//方法1:RTL级描述
module top_module (
input [2:0] SW, // R
input [1:0] KEY, // L and clk
output reg [2:0] LEDR); // Q
wire D0,D1,D2;
assign D0 = KEY[1] ? SW[0] : LEDR[2];
assign D1 = KEY[1] ? SW[1] : LEDR[0];
assign D2 = KEY[1] ? SW[2] : (LEDR[2]^LEDR[1]);
always@(posedge KEY[0])begin
LEDR[0] <= D0;
LEDR[1] <= D1;
LEDR[2] <= D2;
end
endmodule
//方法2:RTL级描述,例化子模块
module top_module (
input [2:0] SW, // R
input [1:0] KEY, // L and clk
output [2:0] LEDR); // Q
wire w1,w2,w3;
assign w1 = KEY[1] ? SW[0] : LEDR[2];
assign w2 = KEY[1] ? SW[1] : LEDR[0];
assign w3 = KEY[1] ? SW[2] : LEDR[1]^LEDR[2];
D_flipflop ins0(w1, KEY[0], LEDR[0]);
D_flipflop ins1(w2, KEY[0], LEDR[1]);
D_flipflop ins2(w3, KEY[0], LEDR[2]);
endmodule
//构建D触发器模块
module D_flipflop(input D, input clk, output Q);
always @(posedge clk)
begin
Q <= D;
end
endmodule
//二路选择器很简单,用三目运算符即可
//方法3:行为级描述,用if-else代替多路选择器
module top_module (
input [2:0] SW, // R
input [1:0] KEY, // L and clk
output [2:0] LEDR); // Q
always@(posedge KEY[0]) begin
if(KEY[1]) begin
LEDR[0] <= SW[0];
LEDR[1] <= SW[1];
LEDR[2] <= SW[2];
end
else begin
LEDR[0] <= LEDR[2];
LEDR[1] <= LEDR[0];
LEDR[2] <= LEDR[1] ^ LEDR[2];
end
end
endmodule
构建一个32位的Galois LFSR,其taps位置为32、22、2和1。
当移位寄存器位数较多,需要使用向量,而不是一一写出32个DFF
module top_module(
input clk,
input reset, // Active-high synchronous reset to 32'h1
output reg [31:0] q
);
integer i;
always@(posedge clk)begin
if(reset == 1'b1)begin
q <= 32'h1;
end else begin
for(i=0; i<32; i=i+1)begin
if(i==21 || i==1 || i==0)begin
q[i] <= q[i+1] ^ q[0];
end else if(i==31)begin
q[i] <= 1'b0 ^ q[0];
end else begin
q[i] <= q[i+1];//q[i] <= q[i+1];
end
end
end
end
endmodule
module top_module (
input clk,
input resetn, // synchronous reset
input in,
output out);
reg [2:0] q;
always@(posedge clk)begin
if(!resetn)begin
{q,out} <= 4'd0;
end else begin
{q,out} <= {in,q};
end
end
endmodule
module MUXDFF (
input clk,
input w, R, E, L,
output Q
);
always@(posedge clk)begin
Q <= L ? R :
E ? w :
Q ;
end
endmodule
module top_module (
input [3:0] SW,
input [3:0] KEY,
output [3:0] LEDR
);
//MUXDFF u_n(.clk(KEY[0]), .w(KEY[3]/LEDR[n]), .E(KEY[1]), .R(SW[n]), .L(KEY[2]), .q(LEDR[n]));
MUXDFF u_3(.clk(KEY[0]), .w(KEY[3] ), .E(KEY[1]), .R(SW[3]), .L(KEY[2]), .q(LEDR[3]));
MUXDFF u_2(.clk(KEY[0]), .w(LEDR[3]), .E(KEY[1]), .R(SW[2]), .L(KEY[2]), .q(LEDR[2]));
MUXDFF u_1(.clk(KEY[0]), .w(LEDR[2]), .E(KEY[1]), .R(SW[1]), .L(KEY[2]), .q(LEDR[1]));
MUXDFF u_0(.clk(KEY[0]), .w(LEDR[1]), .E(KEY[1]), .R(SW[0]), .L(KEY[2]), .q(LEDR[0]));
endmodule
在这个问题中,你将为一个8x1存储器设计一个电路,在这个电路中,写入到存储器是通过移位来完成的,而读取是“随机访问”,就像在一个典型的RAM中一样。然后您将使用该电路实现一个3输入逻辑功能。
首先,用8个d类型触发器创建一个8位移位寄存器。标记为Q[0]到Q[7]。移位寄存器输入称为S,输入Q[0] (MSB先移位)。使能输入enable控制是否移位,扩展电路使其有3个额外的输入A,B,C和一个输出Z。电路的行为应该如下:当ABC为000时,Z=Q[0],当ABC为001时,Z=Q[1],以此类推。你的电路应该只包含8位移位寄存器和多路选择器。(这个电路称为3输入查找表(LUT))。
module top_module (
input clk,
input enable,
input S,
input A, B, C,
output Z );
// The final circuit is a shift register attached to a 8-to-1 mux.
reg [7:0] q;
// Create a 8-to-1 mux that chooses one of the bits of q based on the three-bit number {A,B,C}:
assign Z = q[{A,B,C}];
// Edge-triggered always block: This is a standard shift register (named q) with enable.
// When enabled, shift to the left by 1 (discarding q[7] and and shifting in S).
always@(posedge clk)begin
if(enable == 1'b1)begin
q <= {q[6:0], S};
end else begin
q <= q;
end
end
endmodule