HDLbits day8
2、counters
2.1、Four-bit binary counter
构建一个从 0 到 15(含)计数的 4 位二进制计数器,周期为 16。复位输入是同步的,应将计数器复位为 0。
module top_module ( input clk, input reset, // Synchronous active-high reset output [3:0] q); //always@(posedge clk or posedge reset)//异步 always@(posedge clk)//同步 begin if(reset) q<=4'b0000; else q<=q+4'b0001; end endmodule
2.2、Decade counter
构建一个从 0 到 9(含)计数的十进制计数器,周期为 10。复位输入是同步的,应将计数器复位为 0。
module top_module ( input clk, input reset, // Synchronous active-high reset output [3:0] q); always@(posedge clk) begin //if(reset or q>=4'b1001)错误写法 if(reset || q>=4'b1001) q<=1'b0; else q<=q+1'b1; end endmodule
2.3、Decade counter again
制作一个从 1 到 10 的十年计数器,包括 1 到 10。复位输入是同步的,应将计数器复位为 1。
module top_module ( input clk, input reset, output [3:0] q); //assign q=1'b1;错误写法 initial q=1'b1; always@(posedge clk) begin if(reset || q>=4'd10) q<=1'b1; else q<=q+1'b1; end endmodule
2.4、Slow decade counter
构建一个从 0 到 9 计数的十进制计数器,周期为 10。复位输入是同步的,应该将计数器复位为 0。我们希望能够暂停计数器,而不是总是在每个时钟周期递增,所以slowena输入指示计数器何时应该增加。
1)这是一个带有启用控制信号的常规十进制计数器
2)由图可以看出sslowena信号为高电平时,q+1;sslowena信号为低电平时,q保持
module top_module ( input clk, input slowena, input reset, output [3:0] q); always@(posedge clk) begin if(reset) q<=1'b0; else if(slowena) begin if(q>=4'd9) q<=1'b0; else q<=q+1'b1; end else q<=q; end endmodule
2.5、Counter 1-12
设计一个具有以下输入和输出的 1-12 计数器:
- 复位同步高电平有效复位,强制计数器为 1
- 启用设置高以使计数器运行
- Clk正边沿触发时钟输入
- Q[3:0]计数器的输出
- c_enable, c_load, c_d[3:0]控制信号进入提供的 4 位计数器,因此可以验证正确的操作。
您有以下可用组件:
- 下面的 4 位二进制计数器 ( count4 ),它具有启用和同步并行加载输入(加载的优先级高于启用)。count4模块提供给您。在你的电路中实例化它。
- 逻辑门
module count4( input clk, //时钟 input enable, //启用 input load, //同步并行加载输入 input [3:0] d, // output reg [3:0] Q );
c_enable 、c_load和c_d输出是分别进入内部计数器的enable、load和d输入的信号。它们的目的是允许检查这些信号的正确性。
module top_module ( input clk,//时钟 input reset,//复位 input enable,//启用 output [3:0] Q,//计数 output c_enable, output c_load, output [3:0] c_d ); // //count4 the_counter (clk, c_enable, c_load, c_d /*, ... */ ); assign c_enable=enable;//检测使能端 assign c_load= (Q>=4'd12 && enable==1'b1)|reset; //load为检测是否要加载数据,只有当复位,或者计数到最大值且使能端为1,才可以加载数据 assign c_d=c_load?4'd1:4'd0;//判断有没有加载数据 count4 the_counter (clk, c_enable, c_load, c_d , Q); endmodule
2.6、Counter 1000
从 1000 Hz 时钟导出一个称为OneHertz的 1 Hz 信号,该信号可用于驱动一组小时/分钟/秒计数器的启用信号,以创建数字挂钟。由于我们希望时钟每秒计数一次,因此OneHertz信号必须每秒准确地断言一个周期。使用模 10 (BCD) 计数器和尽可能少的其他门构建分频器。还要从您使用的每个 BCD 计数器输出使能信号(c_enable[0] 为最快的计数器,c_enable[2] 为最慢的)。
为您提供以下 BCD 计数器。Enable必须为高电平才能使计数器运行。复位是同步的并设置为高以强制计数器为零。电路中的所有计数器必须直接使用相同的 1000 Hz 信号。
module bcdcount (
input clk,
input reset,
input enable,
output reg [3:0] Q
);
题目使用3个0-9的计数器来实现时钟分频功能。若仅使用一个bcdcount分频,每计数10次,OneHertz输出为高,那么可以实现10分频,即100Hz。如果counter0每计数10次,counter1计数1次,那么counter1计数10次时OneHertz为高,则可以实现100分频,即10Hz。同理,三个bcdcount可以实现1000分频,即1Hz。
1)采用组合逻辑电路使能三个bcdcount,容易理解,reset不为1时,counter0始终在计数,c_enable[0]=1’b1;
2)仅当counter0计数10次(q0==4’d9)时,counter1计数1次,也就是counter0计数10个时钟周期后,下一时钟周期counter1计数,c_enable[1]=1’b1;
3)当counter1计数10次时(q1==4’d9),counter1会保存q1==4’d9十个时钟周期才会变回0,即需要counter0==4’d9才会再次变化。所以counter2有效的时钟周期为counter1==4’d9的情况下,counted0==4’d9的下一时钟周期。所以q1==4’d9 && q0==4’d9时c_enable[2]=1’b1。
4)根据最终得到的波形图可以看到,要求的OneHertz输出并不是频率为1Hz、占空比为50%的时钟,而是频率为1Hz、宽度为一个clk时钟周期的的脉冲。所以,OneHertz为高的条件为q2==4’d9 && q1==4’d9 && q0==4’d9。
module top_module ( input clk, input reset, output OneHertz, output [2:0] c_enable ); // reg[3:0] q1,q2,q3; assign c_enable[0]=1'b1; assign c_enable[1]= (q1==4'd9)?1'b1:1'b0; assign c_enable[2]= (q1==4'd9 && q2==4'd9)?1'b1:1'b0; assign OneHertz= (q1==4'd9 && q2==4'd9 && q3==4'd9)?1'b1:1'b0; bcdcount counter0 (clk, reset, c_enable[0],q1); bcdcount counter1 (clk, reset, c_enable[1],q2); bcdcount counter2 (clk, reset, c_enable[2],q3); endmodule
2.7、4-digit decimal counter
构建一个4位数的BCD(二进制编码的十进制)计数器。每个十进制数字使用4位进行编码:q[3:0]是个位,q[7:4]是十位,以此类推。对ena[3:1],要输出一个使能信号,指示个十、百、千位什么时候进位,即十、百、千位什么时候加一。
您可能需要实例化一些十进制计数器。
1)q[3:0]从0计数到9,当其为4'd9时,下一个时钟周期,q[7:4]加1;
2)遇到reset=1时,q复位为0
module top_module ( input clk, input reset, // Synchronous active-high reset output [3:1] ena, output [15:0] q); assign ena[1] = (q[3:0]==4'd9)?1'b1:1'b0; assign ena[2] = (q[3:0]==4'd9 && q[7:4]==4'd9)?1'b1:1'b0; assign ena[3] = (q[3:0]==4'd9 && q[7:4]==4'd9 && q[11:8]==4'd9)?1'b1:1'b0; bcd_counter U1(clk,reset,1'b1,q[3:0]); bcd_counter U2(clk,reset,ena[1],q[7:4]); bcd_counter U3(clk,reset,ena[2],q[11:8]); bcd_counter U4(clk,reset,ena[3],q[15:12]); endmodule module bcd_counter( input clk, input reset, input in, output [3:0] counter); always@(posedge clk) begin if(reset) counter<=4'd0; else begin if(counter==4'd9 & in) counter<=4'd0; else counter<=counter+in; end end endmodule
2.8、12-hour clock
设计一个满足12小时计时的时钟系列计数器(包括am/pm指示)。该计数器由一个fast-running clk驱动,每当时钟需要增加计时时,ena信号则会发生脉冲信号(如:每秒一次)。 reset信号复位时钟到12:00 AM。pm信号为0时指示AM,为1时指示PM。hh、mm、ss都为两个BCD digits(8 bits),分别表示小时(01-12)、分钟(00-59)、秒(00-59)。reset 比 enable信号优先级更高,即使未使能,也要执行复位操作。 下面的时序图展示了时钟从11:59:59 AM 到12:00:00 PM的信号变化,以及同步复位和使能操作结果。
注意11:59:59 PM 在下一个时钟周期应为12:00:00 AM ;12:59:59 PM 在下一时钟周期应为 01:00:00 PM 。没有00:00:00这种形式。
module top_module( input clk, input reset, input ena, output pm, output [7:0] hh, output [7:0] mm, output [7:0] ss); reg pm_state; wire pm_ena; reg [3:0] ss_one; reg [3:0] ss_ten; reg [3:0] mm_one; reg [3:0] mm_ten; reg [3:0] hh_one; reg [3:0] hh_ten; always@(posedge clk) begin if(reset) ss_one<=4'd0; else if(ena) begin if(ss_one==4'd9) ss_one<=4'd0; else ss_one<=ss_one+4'd1; end end always@(posedge clk) begin if(reset) ss_ten<=4'd0; else if(ena && (ss_one==4'd9)) begin if(ss_ten==4'd5) ss_ten<=4'd0; else ss_ten<=ss_ten+4'd1; end end always@(posedge clk) begin if(reset) mm_one<=4'd0; else if(ena && (ss_one==4'd9) && (ss_ten==4'd5)) begin if(mm_one==4'd9) mm_one<=4'd0; else mm_one<=mm_one+4'd1; end end always@(posedge clk) begin if(reset) mm_ten<=4'd0; else if(ena && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9)) begin if(mm_ten==4'd5) mm_ten<=4'd0; else mm_ten<=mm_ten+4'd1; end end always@(posedge clk) begin if(reset) hh_one<=4'd2; else if(ena && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9) && (mm_ten==4'd5)) begin if(hh_one==4'd9) hh_one<=4'd0; else if(hh_one==4'd2 && hh_ten==4'd1)//注意12:59:59跳到01:00:00 hh_one<=4'd1; else hh_one<=hh_one+4'd1; end end always@(posedge clk) begin if(reset) hh_ten<=4'd1; else if(ena && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9) && (mm_ten==4'd5)) begin if(hh_one==4'd2 && hh_ten==4'd1)//注意12:59:59跳到01:00:00 hh_ten<=4'd0; else if(hh_one==4'd9) hh_ten<=hh_ten+4'd1; end end always@(posedge clk) begin if(reset) pm_state<=1'b0; else if(pm_ena) pm_state=~pm_state; end assign pm_ena=(ena && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9) && (mm_ten==4'd5) && (hh_one==4'd1) && (hh_ten==4'd1)); assign hh={hh_ten,hh_one}; assign mm={mm_ten,mm_one}; assign ss={ss_ten,ss_one}; assign pm=pm_state; endmodule
3、shift registers
3.1、4-bit shift register
构建一个 4 位移位寄存器(右移),具有异步复位、同步加载和启用。
- areset:将移位寄存器重置为零。
- load :用data[3:0]加载移位寄存器而不是移位。
- ena:右移(q[3]变为零,q[0]移出并消失)。
- q:移位寄存器的内容。
如果load和ena输入都被置位 (1),则load输入具有更高的优先级。
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<=4'd0; else if(load) q<=data; else if(ena) q<={1'b0,q[3],q[2],q[1]}; else q<=q; end endmodule
3.2、Left/right rotator
构建一个 100 位左/右旋转器,具有同步加载和左/右使能。与丢弃移出位并移入零的移位器不同,旋转器从寄存器的另一端移入移出的位。如果启用,旋转器会旋转位并且不会修改/丢弃它们。
- load :用data[99:0]加载移位寄存器而不是旋转。
- ena[1:0]:选择是否旋转以及旋转的方向。
- 2'b01向右旋转一位
- 2'b10向左旋转一位
- 2'b00和2'b11不旋转。
- 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) q<=data; else if(ena==2'b01) //向右旋转一位 q<={q[0],q[99:1]}; else if(ena==2'b10)//向左旋转一位 q<={q[98:0],q[99]}; else q<=q; end endmodule
3.3、Left/right arithmetic shift by 1 or 8
构建一个 64 位算术移位寄存器,同步加载。移位器可以向左和向右移动 1 位或 8 位位置,由数量选择。
算术右移移位寄存器中数字的符号位(在这种情况下为q[63] ),而不是逻辑右移所完成的零。考虑算术右移的另一种方法是,它假设被移动的数字是有符号的并保留符号,因此算术右移将带符号的数字除以 2 的幂。
逻辑左移和算术左移没有区别。
- load :用data[63:0]加载移位寄存器而不是移位。
- 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) q<=data; else if(ena) begin if(amount==2'b00)//左移一位 q<={q[62:0],1'b0}; else if(amount==2'b01)//左移8位 q<={q[55:0],8'b0}; else if(amount==2'b10)//右移1位 q<={q[63],q[63:1]}; else q<={{8{q[63]}},q[63:8]}; end else q<=q; end endmodule
3.4、5-bit LFSR
线性反馈移位寄存器是一种移位寄存器,通常带有几个异或门来产生移位寄存器的下一个状态。Galois LFSR 是一种特殊的移位寄存器,其中带有“抽头”的位置与输出位进行异或运算以产生其下一个值。如果仔细选择抽头位置,则可以将 LFSR 设置为“最大长度”。n 位的最大长度 LFSR 在重复之前循环通过 2 n -1 个状态(永远不会达到全零状态)。
下图显示了一个 5 位最大长度的 Galois LFSR,在位置 5 和 3 处有抽头。(抽头位置通常从 1 开始编号)。请注意,为了保持一致性,我在位置 5 处绘制了 XOR 门,但 XOR 门输入之一是 0。
构建这个 LFSR。复位应将 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) q<=5'h1; else begin q<={1'b0^q[0],q[4],q[3]^q[0],q[2],q[1]}; end end endmodule
3.5、3-bit LFSR
为下面时序电路编写 Verilog 代码(子模块可以,但顶层必须命名为top_module)。假设您要在 DE1-SoC 板上实现电路。将R输入连接到SW开关,将 Clock 连接到KEY[0],并将L连接到KEY[1]。将Q输出连接到红灯LEDR。
该电路是线性反馈移位寄存器(LFSR) 的一个示例。最大周期 LFSR 可用于生成伪随机数,因为它在重复之前循环通过 2 n -1 个组合。全零组合不会出现在此序列中。
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]==1'b1) 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
3.6、32-bit LFSR
在位置 32、22、2 和 1 处构建具有抽头的 32 位 Galois LFSR。
这足够长,以至于您想要使用向量,而不是 32 个 DFF 实例。
module top_module( input clk, input reset, // Active-high synchronous reset to 32'h1 output [31:0] q ); always@(posedge clk) begin if(reset) q<=32'h1; else begin q<={1'b0^q[0],q[31:23],q[22]^q[0],q[21:3],q[2]^q[0],q[1]^q[0]}; end end endmodule
3.7、Shift register
实现以下电路:
module top_module ( input clk, input resetn, // synchronous reset同步复位 input in, output out); reg [2:0] q; always@(posedge clk) begin if(!resetn) {out,q[2:0]}<=4'b0; else begin {out,q[2:0]}<={q[2:0],in}; end end endmodule
3.8、shift register
考虑如下所示 的n位移位寄存器电路:
假设n = 4 ,为移位寄存器编写顶层 Verilog 模块(名为 top_module)。在顶层模块中实例化 MUXDFF 子电路的四个副本。假设您要在 DE2 板上实现电路。
- 将R输入连接到SW开关,
- clk到KEY[0] ,
- E到KEY[1] ,
- L到KEY[2],并且
- w到KEY[3]。
- 将输出连接到红灯LEDR[3:0]。
module top_module ( input [3:0] SW, input [3:0] KEY, output [3:0] LEDR ); // MUXDFF U1(.clk(KEY[0]),.W(LEDR[1]),.R(SW[0]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[0])); MUXDFF U2(.clk(KEY[0]),.W(LEDR[2]),.R(SW[1]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[1])); MUXDFF U3(.clk(KEY[0]),.W(LEDR[3]),.R(SW[2]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[2])); MUXDFF U4(.clk(KEY[0]),.W(KEY[3]),.R(SW[3]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[3])); endmodule module MUXDFF ( input clk, input W,R,E,L, output Q); always@(posedge clk) begin Q<=L?R:E?W:Q; end endmodule
3.9、3-input LUT
在这个问题中,您将为 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))。
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