前言
由于开发板教学内容部分,代码涉及到状态机内容,HDLBits题库只刷到了计数器,因此后续3至4天决定继续刷题,刷完状态机和全部HDLBits题库。今天刷完移位寄存器+更多电路,以下是书写的代码。
题库
Question1:
构建一个 4 位移位寄存器(右移),具有异步复位、同步加载和使能功能。
- areset:将移位寄存器重置为零。
- load:加载带有 data[3:0] 的移位寄存器,而不是移位。
- ena:向右移动(q[3] 变为零,q[0] 移出并消失)。
- 问:移位寄存器的内容。
如果 load 和 ena 输入都置位 (1),则负载输入具有更高的优先级。
Solution:
这里的异步复位,同步释放的内容,建议参考:深入理解复位---同步复位,异步复位,异步复位同步释放(含多时钟域)_画出支持异步复位dff的电路图-CSDN博客
module top_module(
input clk,
input areset, // async active-high reset to zero
input load,
input ena,
input [3:0] data,
output reg [3:0] q);
always @(posedge clk or posedge areset) begin //一般都是写出复位下降沿,下一个时钟周期复位。但是这里的时序电路复位后是即刻复位的,所以写成上升沿。
if(areset)
q [3:0] <= 0;
else if(load)
q <= data;
else if(ena)
q <= {1'b0,q[3:1]}; //向右移位
else
q <= q;
end
endmodule
Question2:
构建一个 100 位左/右旋转器,同步加载和左/右使能。旋转器从寄存器的另一端移入移出位,这与移位器丢弃移出位并移位为零的移位器不同。如果启用,旋转器将旋转位,并且不会修改/丢弃它们。
- load:加载带有数据的移位寄存器[99:0],而不是旋转。
- ena[1:0]:选择是否旋转以及旋转方向。
- 2'B01 向右旋转一位
- 2'b10 向左旋转一位
- 2'B00 和 2'B11 不旋转。
- 问:旋转器的内容。
Solution:
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)
q <= data;
else begin
case(ena)
2'b01:q = {q[0],q[99:1]};//right
2'b10:q = {q[98:0],q[99]};//left
default:q = q;
endcase
end
end
endmodule
Question3:
建立一个具有同步加载功能的 64 位算术移位寄存器。移位寄存器既可以左移,也可以右移,移位位数可以是 1 位,也可以是 8 位。
算术右移时,移入的是移位寄存器中数字(本例中为 q[63])的符号位,而不是逻辑右移时的零。算术右移的另一种思路是,它假定被移位的数字是带符号的,并保留符号,因此算术右移是将带符号的数字除以 2 的幂。
逻辑左移和算术左移没有区别。
加载: 用数据[63:0]加载移位寄存器,而不是移位。
ena: 选择是否移位。
amount: 数量: 选择移位方向和移位量。
2'b00:左移 1 位。
Solution:
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)
q <= data;
else if(ena) begin
case(amount)
2'b00:q = {q[62:0],1'b0};
2'b01:q = {q[55:0],8'b0};
2'b10:q = {q[63],q[63:1]};
2'b11:q = {{8{q[63]}},q[63:8]};
default:q <= q;
endcase
end
end
endmodule
Question4:
线性反馈移位寄存器是一种移位寄存器,通常有几个 XOR 门来产生移位寄存器的下一个状态。伽罗瓦线性反馈移位寄存器(Galois LFSR)是一种特殊的排列方式,其中带有 "抽头 "的比特位置与输出比特进行 XOR,以产生下一个值,而没有抽头的比特位置则进行移位。如果对抽头位置进行精心选择,LFSR 就可以做到 "最大长度"。一个 n 位的最大长度 LFSR 在重复之前会循环经过 2n-1 个状态(永远不会达到全零状态)。
下图显示了一个最大长度为 5 位的伽罗瓦 LFSR,抽头位于第 5 位和第 3 位(抽头位置通常从 1 开始编号)。请注意,为了保持一致,我将 XOR 门画在了第 5 位,但 XOR 门的一个输入端为 0。
amount: 数量: 选择移位方向和移位量。
2'b00:左移 1 位。
2'b01:左移 8 位。
2'b10:右移 1 位。
Solution:
module top_module(
input clk,
input reset, // Active-high synchronous reset to 5'h1
output [4:0] q
);
always @(posedge clk) begin
if(reset)
q <= 5'h1 ;
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
Question5:
编写该顺序电路的 Verilog 代码(子模块也可以,但顶层必须命名为 top_module)。假设要在 DE1-SoC 板上实现该电路。将 R 输入连接到 SW 开关,将 Clock 连接到 KEY[0],将 L 连接到 KEY[1]。将 Q 输出端连接至红灯 LEDR。
Solution:
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[2] ^ LEDR[1];
end
end
endmodule
Question6:
有关解释,请参见 Lfsr5。
构建一个 32 位伽罗瓦 LFSR,在第 32、22、2 和 1 位上设置抽头。
Solution:
module top_module(
input clk,
input reset, // Active-high synchronous reset to 32'h1
output [31:0] q
);
wire q32,q22,q2,q1;
assign q32 = q [0] ^ 1'b0;
assign q22 = q[22] ^ q [0];
assign q2 = q[2] ^ q [0];
assign q1 = q[1] ^ q [0];
always @(posedge clk) begin
if(reset)
q <= 32'h1;
else
q <= {q32,q[31:23],q22,q[21:3],q2,q1};
end
endmodule
Question7:
Solution:
module top_module (
input clk,
input resetn, // synchronous reset
input in,
output out);
reg q1,q2,q3;
always @(posedge clk) begin
if(!resetn)
{out,q1,q2,q3} <= 4'b0;
else
{out,q1,q2,q3} <= {q1,q2,q3,in};
end
endmodule
Question8:
Solution:
module top_module (
input [3:0] SW,
input [3:0] KEY,
output [3:0] LEDR
); //
MUXDFF DE2(
.R (SW ) ,
.clk (KEY[0] ) ,
.E (KEY[1] ) ,
.L (KEY[2] ) ,
.w (KEY[3] ) ,
.Q (LEDR )
);
endmodule
module MUXDFF (
input [3:0] R ,
input clk ,
input E ,
input L ,
input w ,
output [3:0] Q
);
always @(posedge clk) begin
if(L)
Q <= R;
else if(E)begin
Q[3] <= w;
Q[2] <= Q[3];
Q[1] <= Q[2];
Q[0] <= Q[1];
end
else begin
Q[3] <= Q[3];
Q[2] <= Q[2];
Q[1] <= Q[1];
Q[0] <= Q[0];
end
end
endmodule
Question9:
在本题中,您将设计一个 8x1 存储器的电路,其中写入存储器是通过移位完成的,而读取则是 "随机存取",就像典型的 RAM 一样。然后,您将使用该电路实现 3 输入逻辑功能。
首先,用 8 个 D 型触发器创建一个 8 位移位寄存器。将触发器的输出标记为 Q[0]...Q[7]。移位寄存器的输入应称为 S,它馈入 Q[0] 的输入(MSB 先移入)。使能输入控制是否移位。然后,扩展电路,增加 3 个输入端 A、B、C 和一个输出端 Z。电路的行为应如下:当 ABC 为 000 时,Z=Q[0];当 ABC 为 001 时,Z=Q[1],以此类推。您的电路应只包含 8 位移位寄存器和多路复用器。(注:该电路称为 3 输入查找表(LUT))。
Solution:
module top_module (
input clk,
input enable,
input S,
input A, B, C,
output Z );
reg [7:0] Q;
always @(posedge clk) begin
if(enable)
Q <= {Q[6:0],S};
else
Q <= Q;
end
assign Z = Q[{A,B,C}];
endmodule
Question10:
规则 90 是一种具有有趣特性的一维蜂窝自动机。
规则很简单。有一个由单元格(开或关)组成的一维数组。在每个时间步,每个单元格的下一个状态是该单元格当前两个相邻单元格的 XOR。下表是表达这一规则的更详细的方法,其中一个单元格的下一个状态是其自身和两个相邻单元格的函数:
(规则 90 "的名称来源于阅读 "下一状态 "列: 01011010 是十进制 90)。
在该电路中,创建一个 512 单元系统(q[511:0]),每个时钟周期前进一个时间步进。加载输入表示系统的状态应加载数据[511:0]。假设边界(q[-1] 和 q[512])均为零(关闭)。
Solution:
用卡诺图化简真值表,难度一般。其中LEFT向右移动1位,RIGHT向左移动1位。
module top_module(
input clk,
input load,
input [511:0] data,
output [511:0] q );
always @(posedge clk) begin
if(load)
q <= data;
else
q <= {1'b0,q[511:1]}^{q[510:0],1'b0};
end
endmodule
Question11:
规则 110 是一个具有有趣特性(如图灵完备)的一维单元自动机。
有一个一维单元阵列(开或关)。在每个时间步,每个单元的状态都会发生变化。在规则 110 中,每个单元格的下一个状态只取决于它自己和它的两个相邻单元格,如下表所示:
(规则 110 "的名称来源于阅读 "下一状态 "列: 01101110 是十进制 110)。
在该电路中,创建一个 512 单元系统(q[511:0]),每个时钟周期前进一个时间步进。加载输入表示系统的状态应加载数据[511:0]。假设边界(q[-1] 和 q[512])均为零(关闭)。
Solution:
用卡诺图化简真值表,难度一般。其中LEFT向右移动1位,RIGHT向左移动1位。
module top_module(
input clk,
input load,
input [511:0] data,
output [511:0] q
);
always @(posedge clk) begin
if(load)
q <= data;
else
q <= (~({q[510:0],1'b0})&q[511:0])|(q[511:0]&~({1'b0,q[511:1]}))|(~({1'b0,q[511:1]})&({q[510:0],1'b0}))|(~(q[511:0])&({q[510:0],1'b0}));
end
endmodule
Question12:
康威生命游戏是一种二维细胞自动机。
游戏 "在一个二维单元格上进行,每个单元格要么是 1(活),要么是 0(死)。在每个时间步长内,每个单元格的状态变化取决于它有多少个邻居:
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 网格由长度为 256 的矢量表示,其中每行 16 个单元由一个子矢量表示:q[15:0] 表示第 0 行,q[31:16] 表示第 1 行,等等。
加载: 在下一个时钟沿将数据加载到 q 中,用于加载初始状态。
q:16x16 的游戏当前状态,每个时钟周期更新一次。
游戏状态应在每个时钟周期前进一个时间步。
约翰-康威(John Conway),数学家和生命游戏细胞自动机的创造者,于 2020 年 4 月 11 日因 COVID-19 病逝。
Solution:
module top_module(
input clk,
input load,
input [255:0] data,
output [255:0] q );
reg [3:0] sum;
integer i ;
always @(posedge clk) begin
if(load)
q <= data;
else begin
for(i=0;i<256;i++) begin//这里采用枚举法,分四种情况。
if(i == 0) begin //情况1:四个角落,左上角,右上角,左下角,右下角
sum = q[15] + q[1] + q[31] + q[16] + q[17] + q[255] + q[240] + q[241];
end
else if(i == 15) begin
sum = q[254] + q[255] + q[240] + q[14] + q[0] + q[30] + q[31] + q[16];
end
else if(i == 240)begin
sum = q[239] + q[224] + q[225] + q[255] + q[241] + q[15] + q[0] + q[1];
end
else if(i == 255)begin
sum = q[238] + q[239] + q[224] + q[254] + q[240] + q[14] + q[15] + q[0];
end
else if(i>0 && i<15)begin//情况2:第0行,第15行
sum = q[i+240] + q[i+239] + q[i+241] + q[i-1] + q[i+1] + q[i+15] + q[i+16] + q[i+17];
end
else if(i>240 && i<255)begin
sum = q[i-17] + q[i-16] + q[i-15] + q[i-1] + q[i+1] + q[i-241] + q[i-240] + q[i-239];
end
else if(i%16 == 0)begin//情况3:第0列,第15列
sum = q[i-1] + q[i-16] + q[i-15] + q[i+15] + q[i+1] + q[i+31] + q[i+16] + q[i+17];
end
else if(i%16 == 15)begin
sum = q[i-17] + q[i-16] + q[i-31] + q[i-1] + q[i-15] + q[i+15] + q[i+16] + q[i+1];
end
else begin
sum = q[i-17] + q[i-16] + q[i-15] + q[i-1] + q[i+1] + q[i+15] + q[i+16] + q[i+17];
end
case(sum)
4'd2 :q[i] <= q[i] ;
4'd3 :q[i] <= 1 ;
default:q[i] <= 0 ;
endcase
end
end
end
endmodule
注:
本题,遇到了一个非常严重的问题,即阻塞赋值和非阻塞赋值,我原先的代码是这样的。即循环中的if语句我用的是非阻塞赋<=(时序电路),而case语句我用的阻塞赋值=(组合逻辑),发生了报错。首先在for循环语句的时序逻辑中必须使用阻塞赋值,不能使用非阻塞赋值。如果用阻塞赋值,在循环中,这里sum会被一直赋值,会成为0或者不定态。
具体内容参考:FPGA学习笔记:Verilog中for循环的阻塞赋值与非阻塞赋值_时序逻辑下的for循环赋值是什么-CSDN博客
module top_module(
input clk,
input load,
input [255:0] data,
output [255:0] q );
reg [3:0] sum;
integer i ;
always @(posedge clk) begin
if(load)
q <= data;
else begin
for(i=0;i<256;i++) begin//这里采用枚举法,分四种情况。
if(i == 0) begin //情况1:四个角落,左上角,右上角,左下角,右下角
sum <= q[15] + q[1] + q[31] + q[16] + q[17] + q[255] + q[240] + q[241];
end
else if(i == 15) begin
sum <= q[254] + q[255] + q[240] + q[14] + q[0] + q[30] + q[31] + q[16];
end
else if(i == 240)begin
sum <= q[239] + q[224] + q[225] + q[255] + q[241] + q[15] + q[0] + q[1];
end
else if(i == 255)begin
sum <= q[238] + q[239] + q[224] + q[254] + q[240] + q[14] + q[15] + q[0];
end
else if(i>0 && i<15)begin//情况2:第0行,第15行
sum <= q[i+240] + q[i+239] + q[i+241] + q[i-1] + q[i+1] + q[i+15] + q[i+16] + q[i+17];
end
else if(i>240 && i<255)begin
sum <= q[i-17] + q[i-16] + q[i-15] + q[i-1] + q[i+1] + q[i-241] + q[i-240] + q[i-239];
end
else if(i%16 == 0)begin//情况3:第0列,第15列
sum <= q[i-1] + q[i-16] + q[i-15] + q[i+15] + q[i+1] + q[i+31] + q[i+16] + q[i+17];
end
else if(i%16 == 15)begin
sum <= q[i-17] + q[i-16] + q[i-31] + q[i-1] + q[i-15] + q[i+15] + q[i+16] + q[i+1];
end
else begin
sum <= q[i-17] + q[i-16] + q[i-15] + q[i-1] + q[i+1] + q[i+15] + q[i+16] + q[i+17];
end
case(sum)
4'd2 :q[i] = q[i] ;
4'd3 :q[i] = 1 ;
default:q[i] = 0 ;
endcase
end
end
end
endmodule
在Verilog中,case
语句是用来进行多路分支选择的一种结构。在case
语句中,可以使用非阻塞赋值<=
, 也可以使用阻塞赋值=
。
阻塞赋值在case
语句中的使用方法如下:
always @(*) begin
case (sel)
'b00: out = a;
'b01: out = b;
'b10: out = c;
default: out = d;
endcase
end
在这个例子中,out
的赋值是阻塞的,即在同一个always块内,下一个赋值会等到当前赋值的结束后才进行。
非阻塞赋值在case
语句中的使用方法如下:
always @(posedge clk) begin
case (sel)
'b00: out <= a;
'b01: out <= b;
'b10: out <= c;
default: out <= d;
endcase
end
在这个例子中,out
的赋值是非阻塞的,即在时钟沿到来时,所有的非阻塞赋值同时进行。
注意:在同一个always块中,不能同时使用阻塞赋值和非阻塞赋值来对同一个变量进行赋值。
参考内容
HDLBits刷题网站:HDLBits (01xz.net)
标签:clk,always,HDLBits,module,寄存器,input,移位 From: https://blog.csdn.net/weixin_58164636/article/details/140556483