数电第7周周结_by_yc
一、通用双向移位寄存器:
- 功能描述:
4位的双向移位寄存器,含控制输入端(ctrl)、串行输入端(Dsl、Dsr)、4个并行输入端和4个并行输出端,要求实现5种功能:异步置零、同步置数、左移、右移和保持原状态不变,功能如下:
ctrl | action |
---|---|
00 | 保持 |
01 | 右移 |
10 | 左移 |
11 | 并行输入 |
- 设计方案:
①异步置零:复位信号需放入敏感列表;
②优先级:reset>ctrl;
③ctrl的多方案可以用case来完成 - 关键代码:
always@(posedge clk or negedge reset) begin
if(~reset)
Dout <= 4'b0000; //Asynchronous zero setting
else begin
case(ctrl)
2'b00: Dout <= Dout; //remain
2'b01: Dout <= {Dsr, Dout[3:1]}; //shift right
2'b10: Dout <= {Dout[2:0], Dsl}; //Shift left
2'b11: Dout <= Din; //Parallel input
endcase
end
end
- 仿真验证:
分别控制实现保持、右移、左移和并行输入。
initial begin
clk=0;
forever #10 clk=~clk;
end
initial begin
reset=0; Dsl=0; Dsr=1; ctrl=2'b00; Din=4'b1111;
#100 reset=1; Dsl=0; Dsr=1; ctrl=2'b01; Din=4'b1111;
#100 reset=1; Dsl=0; Dsr=0; ctrl=2'b10; Din=4'b1111;
#100 reset=1; Dsl=0; Dsr=0; ctrl=2'b11; Din=4'b0001;
#100 $stop;
end
输出波形如下图所示,符合题意。
- 综合结果:
- 总结反思:
看好模块名称!!!
二、带控制组的触发器组:
-
功能描述:
16位D触发器,以byteena来控制寄存器的高低字节是否被写入。byteena[1]控制高字节d[15:8],而byteena[0]控制低字节d[7:0].resetn是低电平有效的同步复位信号.所有DFF由时钟的上升沿触发. -
设计方案:
①普通的16位同步复位D触发器,resetn不在敏感列表中
②优先级:reset>byteena;
③byteena的多方案可以用case来完成 -
关键代码:
always @(posedge clk) begin
if(~resetn)
q <= 16'b0000000000000000;
else begin
case(byteena)
2'b00: q <= q;
2'b01: q <= {q[15:8],d[7:0]};
2'b10: q <= {d[15:8],q[7:0]};
2'b11: q <= d;
endcase
end
end
- 仿真验证:
依次设计进行:不赋值、低位赋值、高位赋值、全部赋值
initial begin
clk=0;
forever #10 clk=~clk;
end
initial begin
resetn=1; byteena=2'b00; d=16'b1010101111001101;
#100 resetn=1; byteena=2'b01; d=16'b1010101111001101;
#100 resetn=1; byteena=2'b10; d=16'b1010101111001101;
#100 resetn=1; byteena=2'b11; d=16'b1010101111001101;
#100 resetn=0;
#100 $stop;
end
如图,在第一个大周期内,输出悬空,未赋值;第二个时钟周期内,完成了低位赋值,高位悬空;三四周期依次完成了高位赋值和全赋值,符合题意。
- 综合结果:
三、算数左右移:
- 功能描述:
64位算术移位寄存器,具有同步load数据功能,具体移位由ctrl控制,由下表所示:
ctrl | action |
---|---|
00 | 算术左移1位 |
01 | 算术左移8位 |
10 | 算术右移1位 |
11 | 算术右移8位 |
- 设计方案:
①算术右移时,需要复制最高位,可以通过拼接运算符来完成;算术左移同逻辑左移
②优先级:load>ena>ctrl;
③ctrl的多方案可以用case来完成 - 关键代码:
always@(posedge clk) begin
if(load)
q <= data;
else if(ena) begin
case(ctrl)
2'b00: q <= {q[62:0], 1'b0};
2'b01: q <= {q[55:0], 8'b0};
2'b10: q <= {1'b0, q[63:1]};
2'b11: q <= {8'b0, q[63:8]}; //unsigned number
endcase
end
end
- 仿真验证:
分别设计进行加载数据、算术左移1、算术左移8、算术右移1、算术右移8、不移位。
initial begin
clk=0;
forever #10 clk=~clk;
end
initial begin
load=1; ena=1; ctrl=2'b00; data=16'h1111111111111111;
#20 load=0; ena=1; ctrl=2'b01; data=16'h1111111111111111;
#20 load=0; ena=1; ctrl=2'b10; data=16'h1111111111111111;
#20 load=0; ena=1; ctrl=2'b11; data=16'h1111111111111111;
#20 load=0; ena=0; ctrl=2'b10; data=16'h1111111111111111;
end
得到对应波形如下图所示,符合预设。
- 综合结果:
- 总结分析:
拼接运算符的正确应用:q <= {{8{q[63]}}, q[63:8]}。
四、8位同步二进制加减法计数器:
-
功能描述:
8位同步二进制加减法计数器,输入为时钟端clk(下降沿有效)和异步清除端rstn(低电平有效),加减控制端updown,当updown为1时执行加法计数,为0时执行减法计数;输出为进位端C和8位计数输出端Q -
设计方案:
①异步置零:复位信号需放入敏感列表;
②优先级:rstn>updown;
③updown的多方案可以用case来完成.
④进位端C只有在从255->0时才为高电平,其余为低电平,可以将当前值是否为255作为一级判断。 -
关键代码:
always @(negedge clk) begin
if(~rstn) begin Q <= 8'b0;C=0; end
else if(Q == 8'b11111111) begin
if(updown == 1) begin C=1; Q=8'b00000000; end
else begin C=0; Q=8'b11111110; end
end
else begin
case(updown)
1'b1: begin Q = Q + 1; C=0; end
1'b0: begin Q = Q - 1; C=0; end
endcase
end
end
- 仿真验证:
设计含加减法和255->0的testbench
initial begin
clk=0;
forever #10 clk=~clk;
end
initial begin
rstn=0; updown=1;
#20 rstn=1; updown=1;
#20 rstn=1; updown=0;
#40 rstn=1; updown=1;
#100 $stop;
end
相应的波形图如下,加减法合题意,且由255->0时,相应的进位输出为1.
- 综合结果:
五、分频器:
-
功能描述:
N分频时钟,50%占空比,上升沿采样,异步低电平复位,不可采用电平触发,同一敏感列表不可同时出现同一个信号的上升沿和下降沿。 -
设计方案:
①异步复位:rstn信号出现在敏感列表中;
②因为对于奇偶性不同的N值,涉及到分别在上升沿和下降沿进行翻转,故需对N值得奇偶进行讨论:
N为偶数:
为实现能够对时钟频率进行分频,则需要对时钟的翻转次数进行计数,这里采用对时钟上升沿进行计数,cnt每加一,则说明经过了一个时钟周期,如按图中需分四倍频,则需经过两个时钟周期,进行一次反转,四倍频即为\({cnt==1}\),则进行一次翻转,推广到偶数的N,则为每逢\({cnt==(N>>1)-1}\),进行一次翻转。
N为奇数:
而奇数的特殊点在于涉及到在时钟的上升沿和下降沿分别翻转,则可将其等效为两个偶倍频的并.
如图中\({odd\_clk\_out\_1}\),\({odd\_clk\_out\_2}\),其中前者在时钟上升沿进行翻转;后者在时钟下降沿进行翻转。
同时依旧在上升沿进行计数(图上cnt有一定误差,画不到中间的部分😭),对于图中五倍频来说,前者在\({cnt==0|cnt==2}\)进行翻转,后者在\({cnt==1|cnt==3}\)进行翻转,
推广到奇数的N,则有前者在\({cnt==0|cnt==N>>1}\)进行翻转,后者在\({cnt==1|cnt==N>>1+1}\)进行翻转。
- 关键代码:
//even
always@(posedge clk, negedge rstn) begin
if(!rstn)
even_cnt <= 1'b0;
else if(even_cnt == (N>>1'b1)-1'b1)
even_cnt <= 1'b0;
else
even_cnt <= even_cnt + 1'b1;
end
always@(posedge clk, negedge rstn) begin
if(!rstn)
even_clk_out <= 1'b0;
else if(even_cnt == (N>>1'b1)-1'b1)
even_clk_out <= ~even_clk_out;
else
even_clk_out <= even_clk_out;
end
//odd
always@(posedge clk, negedge rstn) begin
if(!rstn)
odd_cnt <= 1'b0;
else if(odd_cnt == N-1'b1)
odd_cnt <= 1'b0;
else
odd_cnt <= odd_cnt + 1'b1;
end
always@(posedge clk, negedge rstn) begin
if(!rstn)
odd_clk_out1 <= 1'b0;
else if(odd_cnt == N>>1'b1 | odd_cnt == 1'b0)
odd_clk_out1 <= ~odd_clk_out1;
else
odd_clk_out1 <= odd_clk_out1;
end
always@(negedge clk, negedge rstn) begin
if(!rstn)
odd_clk_out2 <= 1'b0;
else if(odd_cnt == (N>>1'b1)+1'b1 | odd_cnt == 1'b1)
odd_clk_out2 <= ~odd_clk_out2;
else
odd_clk_out2 <= odd_clk_out2;
end
assign odd_clk_out = odd_clk_out1 | odd_clk_out2;
assign clk_out = (N%2==0) ? even_clk_out : odd_clk_out;
- 仿真验证:
由代码,我们只需要控制生成时钟信号clk和复位信号rstn即可。
initial begin
clk = 0;
forever #10 clk = ~clk;
end
initial begin
rstn=0;
#20 rstn=1;
end
相应地,五倍频的仿真波形结果如下,符合要求。
- 综合结果:
- 总结分析:
①:总结规律->设计实现;
②:优先级:算术运算符>移位运算符,想要先进行移位时,应加括号。
六、异步四位二进制加计数器:
- 功能描述:
每个D触发器的Q非端连接到D端,实现翻转功能。计数时钟clk加至触发器FF0的时钟脉冲输入端,每输入一个计数脉冲,FF0翻转一次。
FF1-FF3都以前一级触发器的Q端作为触发信号,当Q0由1变0时,FF1翻转,其余类推。
-
设计方案:
①设计基本的D触发器作为FF,例化4次得到功能实现;
②据图连接例化端口。 -
关键代码:
module FF(
input clk,
input rstn,
input d,
output reg q);
always @(negedge clk) begin
if(!rstn) q <= 0;
else q <= d;
end
endmodule
module asyn_counter(
input clk,
input rstn,
output q0,
output q1,
output q2,
output q3);
FF FF0(
.clk(clk),
.rstn(rstn),
.d(~q0),
.q(q0));
FF FF1(
.clk(q0),
.rstn(rstn),
.d(~q1),
.q(q1));
FF FF2(
.clk(q1),
.rstn(rstn),
.d(~q2),
.q(q2));
FF FF3(
.clk(q2),
.rstn(rstn),
.d(~q3),
.q(q3));
endmodule
- 仿真验证:
只需控制生成时钟信号和复位信号
initial begin
clk = 0;
forever #10 clk=~clk;
end
initial begin
rstn=0;
#20 rstn=1;
#100 $stop;
end
对应波形图如下,正确完成从零开始的下降沿计数。
- 综合结果: