赞
踩
早期的硬盘为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
各单元的下一状态是此时当前单元相邻两位的异或。
在这个电路中,创建一个512单元系统(q(511:0)),并在每个时钟周期中前进一个时间步长。加载(load)表明系统的状态应该加载data[511:0]至q中,假设边界(q[0]和q[512])都为零。
//方法1,数组类似于水管,for循环类似于套在管子上的滑轨 module top_module( input clk, input load, input [511:0] data, output [511:0] q ); integer i; always@(posedge clk)begin if(load == 1'b1)begin q <= data; end else begin q[0] <= q[1] ^ 1'b0; q[511] <= q[510] ^ 1'b0; for(i=1; i<511; i=i+1)begin //i:510~1 q[i] <= q[i+1] ^ q[i-1]; end end end endmodule
//方法二,给数组的每对数进行异或 module top_module( input clk, input load, input [511:0] data, output reg [511:0] q); always @(posedge clk) begin if (load) q <= data; // Load the DFFs with a value. else begin // left right // neighbour neighbour q <= {1'b0, q[511:1]} ^ {q[510:0], 1'b0} ; end end endmodule
与上题类似,状态转移条件发生变化,如下图所示:
根据真值表写出卡诺图并化简:OUT = Center ^ Right + ( ~Left &Center )
module top_module( input clk, input load, input [511:0] data, output [511:0] q ); integer i; always@(posedge clk)begin if(load == 1'b1)begin q <= data; end else begin q[0] <= (q[0]) || (q[0]& ~q[1]);; q[511] <= (q[511]^q[510]) || (q[511]);; for(i=1;i<511;i=i+1)begin//i = 1; i<511;i=i+1 q[i] <= (q[i]^q[i-1]) || (q[i]& ~q[i+1]); end end end endmodule
作为前两题的升级版,本题的变换工作在一个二维矩阵上,是一个二维序列生成器。
游戏规则如下:元素的下一个状态取决于当前状态九宫格中的 8 个邻居元素中 1 的个数,当邻居有 n 个 1 时:
0-1 ,元素变为 0
2 ,元素保持不变
3 ,元素变为 1
4+ ,元素变为 0
方便做题起见,本题中的这个二维矩阵设定为 16x16,广义上可以是无限的。
为了让事情变得更加有趣,这个16x16 矩阵的边界进行循环处理,回卷到对边,打个比方,上边界的上一行为下边界,左边界的左一列为右边界。
上下边界回卷示意,左右边界同理所以对元素 (0,0) 来说,共有 8 个邻居 : (15,1), (15,0), (15,15), (0,1), (0,15), (1,1), (1,0) 以及 (1,15)。
这个 16x16 矩阵表示为 256bit 长度的向量 q,其中 q[15:0] 代表第一行,q[31:16] 代表第二行,以此类推。
HDLBit 支持使用 SystemVerilog,所以你也可以使用二维向量表示这个矩阵。load 信号有效时,更新 q 信号值为初始值 data, q 每个周期变换一次。
统计矩阵中每个元素的 8 -相邻元素中 1 的个数
根据相邻元素中的 1 的个数,决定元素下一状态的值
使用组合逻辑,采用相加的方式计算相邻元素中 1 的个数,使用一个 256 长的序列来记录每个元素相邻元素中 1 的个数,最大为 8 个,所以每个元素使用 3 bit 来记录。
wire [2:0] nghbr_num [255:0];
在统计时,需要处理边界绕回的的特殊情况。对于边界上的元素,根据绕回的规则确立边界。需要特殊处理的是第一/最后一行/列。在编写 Verilog 代码时,可以对这几种情况分别确立边界。
因为不想代表显得太冗长,这里引入了 4 个整形变量 idx_i_d, idx_i_u, idx_j_r, idx_j_l ,在不同的情况下,来确立四条边界。
idx_i_u = (i == 0) ? i-1+16 :i-1; //up idx
idx_i_d = (i == 15)? i+1-16 :i+1; //down idx
idx_j_l = (j == 0) ? j-1+16 :j-1; //left idx
idx_j_r = (j == 15)? j+1-16 :j+1; //right idx
使用时序逻辑,根据统计的结果,决定下一周期元素的值。
输出最终结果时,将二维信号重新转换为一维信号,Verilog 不支持直接在一维/二维信号之间赋值。
module top_module( input clk, input load, input [255:0] data, output [255:0] q ); reg [15:0] q_2d[15:0]; wire [2:0] nghbr_num [255:0]; int idx_i_u, idx_i_d, idx_j_l, idx_j_r; int i,j; //count num of neighbours always@(*)begin for(i=0; i<16; i=i+1)begin for(j=0; j<16; j=j+1)begin idx_i_u = (i == 0) ? i-1+16 : i-1;//up idx idx_i_d = (i == 15) ? i+1-16 : i+1;//down idx idx_j_l = (j == 0) ? j-1+16 : j-1;//left idx idx_j_r = (j == 15) ? j+1-16 : j+1;//right idx nghbr_num[i*16+j] = q_2d[idx_i_u][idx_j_l] + q_2d[idx_i_u][j] + q_2d[idx_i_u][idx_j_r] + q_2d[i][idx_j_l] + q_2d[i][idx_j_r] + q_2d[idx_i_d][idx_j_l] + q_2d[idx_i_d][j] + q_2d[idx_i_d][idx_j_r]; end end end //next state transform base on num of neighours always@(posedge clk)begin if(load == 1'b1)begin:init for(i=0; i<16; i=i+1)begin for(j=0; j<16; j=j+1)begin q_2d[i][j] <= data[i*16+j]; end end end else begin:set_val for(i=0; i<16; i=i+1)begin for(j=0; j<16; j=j+1)begin if(nghbr_num[i*16+j] < 2)begin q_2d[i][j] <= 'd0; end else if(nghbr_num[i*16+j] == 3)begin q_2d[i][j] <= 'd1; end else if(nghbr_num[i*16+j] > 3)begin q_2d[i][j] <= 'd0; end else begin q_2d[i][j] <= q_2d[i][j]; end end end end end //output always@(*)begin for(i=0; i<16; i=i+1)begin for(j=0; j<16; j=j+1)begin q[i*16+j] = q_2d[i][j]; end end end endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。