
【HDLBits答案及思路(仅供参考,3 Circuits)】
HDLBits学习笔记,答案分享
目录(点击题目直达)
- 0 引言
- 3 Circuits
- 3.1 Combinational Logic
- 3.1.1 Basic Gates
- ● Wire (Exams/m2014 q4h)
- ● GND (Exams/m2014 q4i)
- ● NOR (Exams/m2014 q4e)
- ● Another gate (Exams/m2014 q4f)
- ● Two gates (Exams/m2014 q4g)
- ● More logic gates (Gates)
- ● 7420 chip (7420)
- ● Truth tables (Truthtable1)
- ● Two-bit equality (Mt2015 eq2)
- ● Simple circuit A (Mt2015 q4a)
- ● Simple circuit B (Mt2015 q4b)
- ● Combine circuits A and B (Mt2015 q4)
- ● Ring or vibrate? (Ringer)
- ● Thermostat (Thermostat)
- ● 3-bit population count (Popcount3)
- ● Gates and vectors (Gatesv)
- ● Even longer vectors (Gatesv100)
- 3.1.2 Multiplexers
- 3.1.3 Arithmetic Circuits
- 3.1.4 Karnaugh Map to Circuit
- 3.2 Sequential Logic
- 3.2.1 Latches and Flip-Flops
- ● D flip-flop (Dff)
- ● D flip-flops (Dff8)
- ● DFF with reset (Dff8r)
- ● DFF with reset value (Dff8p)
- ● DFF with asynchronous reset (Dff8ar)
- ● DFF with byte enable (Dff16e)
- ● D Latch (Exams/m2014 q4a)
- ● DFF (Exams/m2014 q4b)
- ● DFF (Exams/m2014 q4c)
- ● DFF+gate (Exams/m2014 q4d)
- ● Mux and DFF (Mt2015 muxdff)
- ● Mux and DFF (Exams/2014 q4a)
- ● DFFs and gates (Exams/ece241 2014 q4)
- ● Create circuit from truth table (Exams/ece241 2013 q7)
- ● Detect an edge (Edgedetect)
- ● Detect both edges (Edgedetect2)
- ● Edge capture register (Edgecapture)
- ● Dual-edge triggered flip-flop (Dualedge)
- 3.2.2 Counters
- 3.2.3 Shift Registers
- 3.2.4 More Circuits
- 3.2.5 Finite State Machines
- ● Simple FSM 1 (asynchronous reset) (Fsm1)
- ● Simple FSM 1 (synchronous reset) (Fsm1s)
- ● Simple FSM 2 (asynchronous reset) (Fsm2)
- ● Simple FSM 2 (synchronous reset) (Fsm2s)
- ● Simple state transitions 3 (Fsm3comb)
- ● Simple one-hot state transitions 3 (Fsm3onehot)
- ● Simple FSM 3 (asynchronous reset) (Fsm3)
- ● Simple FSM 3 (synchronous reset) (Fsm3s)
- ● Design a Moore FSM (Exams/ece241 2013 q4)
- ● Lemmings 1 (Lemmings1)
- ● Lemmings 2 (Lemmings2)
- ● Lemmings 3 (Lemmings3)
- ● Lemmings 4 (Lemmings4)
- ● One-hot FSM (Fsm onehot)
- ● PS/2 packet parser (Fsm ps2)
- ● PS/2 packet parser and datapath (Fsm ps2data)
- ● Serial receiver (Fsm serial)
- ● Serial receiver and datapath (Fsm serialdata)
- ● Serial receiver with parity checking (Fsm serialdp)
0 引言
最近通过HDLBits学习verilog,写下此文记录下学习过程和一些心得,文中不对之处请各位批评指正。此文仅为3 Circuits一章题目,更多题目请见:
【HDLBits答案及思路(仅供参考,1 Getting Started与2 Verilog Language)】
【HDLBits答案及思路(仅供参考,4 Verification: Reading Simulations与5 Verification: Writing Testbenches)(暂未更新)】
所有代码都通过了HDLBits网站验证,但是可能在写文修改格式时出现一些问题,所以发现有不正确之处,请联系我修改。同时,我的方法不一定是最优解决方案,仅供参考,如果朋友有更好的解决思路,也希望能够不吝赐教。注:HDLBits中模块端口声明并没有指定数据类型,我也不打算修改。
文中括号外标题对应于HDLBits首页中的标题,括号内的标题对应于具体题目左上角的标题,如● Getting Started (Step one),Getting Started是进入主页面后看到的标题,Step one是做题时左上角显示的标题。
3 Circuits
3.1 Combinational Logic
3.1.1 Basic Gates
● Wire (Exams/m2014 q4h)
module top_module (
input in,
output out);
assign out=in;
endmodule
● GND (Exams/m2014 q4i)
module top_module (
output out);
assign out=1'b0;
endmodule
● NOR (Exams/m2014 q4e)
module top_module (
input in1,
input in2,
output out);
assign out=~(in1 | in2);
endmodule
● Another gate (Exams/m2014 q4f)
module top_module (
input in1,
input in2,
output out);
assign out=in1 & ~in2;
endmodule
● Two gates (Exams/m2014 q4g)
module top_module (
input in1,
input in2,
input in3,
output out);
assign out=(~(in1^in2))^in3;
endmodule
● More logic gates (Gates)
module top_module(
input a, b,
output out_and,
output out_or,
output out_xor,
output out_nand,
output out_nor,
output out_xnor,
output out_anotb
);
assign out_and=a&b;
assign out_or=a|b;
assign out_xor=a^b;
assign out_nand=~(a&b);
assign out_nor=~(a|b);
assign out_xnor=~(a^b);
assign out_anotb=a&~b;
endmodule
● 7420 chip (7420)
module top_module (
input p1a, p1b, p1c, p1d,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y );
assign p1y=~(p1a & p1b & p1c & p1d);
assign p2y=~(p2a & p2b & p2c & p2d);
endmodule
● Truth tables (Truthtable1)
module top_module(
input x3,
input x2,
input x1, // three inputs
output f // one output
);
assign f= ~x3&x2&~x1 | ~x3&x2&x1 | x3&~x2&x1 | x3&x2&x1;
endmodule
● Two-bit equality (Mt2015 eq2)
module top_module (
input [1:0] A,
input [1:0] B,
output z );
assign z=(A==B)?1'b1:1'b0;
endmodule
● Simple circuit A (Mt2015 q4a)
module top_module (
input x,
input y,
output z);
assign z=(x^y)&x;
endmodule
● Simple circuit B (Mt2015 q4b)
module top_module (
input x,
input y,
output z );
assign z=~(x^y);
endmodule
● Combine circuits A and B (Mt2015 q4)
module top_module (
input x,
input y,
output z);
//A电路为(x^y)&x
//B电路为~(x^y)
assign z=(((x^y)&x)|(~(x^y))) ^ (((x^y)&x)&(~(x^y)));
endmodule
注:A电路为(x^y)&x,B电路为~(x^y)。
● Ring or vibrate? (Ringer)
module top_module (
input ring,
input vibrate_mode,
output ringer, // Make sound
output motor // Vibrate
);
assign motor=vibrate_mode & ring;
assign ringer=~vibrate_mode & ring;
endmodule
注:verilog为硬件语言,其代码思路应该是从输出到输入(输出在输入为何种情况才成立,即等于1),而非软件编程的输入到输出(输入为A时输出是什么)。
● Thermostat (Thermostat)
module top_module (
input too_cold,
input too_hot,
input mode,
input fan_on,
output heater,
output aircon,
output fan
);
assign heater=(mode==1&&too_cold==1)?1'b1:1'b0;
assign aircon=(mode==0&&too_hot==1)?1'b1:1'b0;
assign fan=(heater==1||aircon==1||fan_on==1)?1'b1:1'b0;
endmodule
● 3-bit population count (Popcount3)
module top_module(
input [2:0] in,
output [1:0] out );
integer i;
always@(*)begin
out=2'b0;
for(i=0;i<3;i=i+1)begin
out=in[i]==1?out+1'b1:out;
end
end
endmodule
● Gates and vectors (Gatesv)
module top_module(
input [3:0] in,
output [2:0] out_both,
output [3:1] out_any,
output [3:0] out_different );
assign out_both=in[2:0] & in[3:1];
assign out_any=in[3:1] | in[2:0];
assign out_different=in[3:0] ^ {in[0],in[3:1]};
endmodule
● Even longer vectors (Gatesv100)
module top_module(
input [99:0] in,
output [98:0] out_both,
output [99:1] out_any,
output [99:0] out_different );
assign out_both=in[98:0] & in[99:1];
assign out_any=in[99:1] | in[98:0];
assign out_different=in[99:0] ^ {in[0],in[99:1]};
endmodule
3.1.2 Multiplexers
● 2-to-1 multiplexer (Mux2to1)
module top_module(
input a, b, sel,
output out );
assign out=sel==0?a:b;
endmodule
● 2-to-1 bus multiplexer (Mux2to1v)
module top_module(
input [99:0] a, b,
input sel,
output [99:0] out );
assign out=sel==0?a:b;
endmodule
● 9-to-1 multiplexer (Mux9to1v)
module top_module(
input [15:0] a, b, c, d, e, f, g, h, i,
input [3:0] sel,
output [15:0] out );
always@(*)begin
case(sel)
4'd0:out=a;
4'd1:out=b;
4'd2:out=c;
4'd3:out=d;
4'd4:out=e;
4'd5:out=f;
4'd6:out=g;
4'd7:out=h;
4'd8:out=i;
default:out=16'hffff;
endcase
end
endmodule
● 256-to-1 multiplexer (Mux256to1)
module top_module(
input [255:0] in,
input [7:0] sel,
output out );
assign out=in[sel];
endmodule
● 256-to-1 4-bit multiplexer (Mux256to1v)
module top_module(
input [1023:0] in,
input [7:0] sel,
output [3:0] out );
/*//方法一:索引偏移
assign out=in[sel*4+3-:4];*/
//方法二:移位(有截断warning)
assign out=in>>sel*4;
endmodule
注:在assign中不能使用[sel*4+3:sel*4]进行索引赋值,因为此时sel为wire类型,不是定值。应该使用[sel*4+3-:4]索引,表示从sel*4+3位开始,向右数4位为止。
3.1.3 Arithmetic Circuits
● Half adder (Hadd)
module top_module(
input a, b,
output cout, sum );
assign {cout,sum}=a+b;
endmodule
● Full adder (Fadd)
module top_module(
input a, b, cin,
output cout, sum );
assign {cout,sum}=a+b+cin;
endmodule
● 3-bit binary adder (Adder3)
module top_module(
input [2:0] a, b,
input cin,
output [2:0] cout,
output [2:0] sum );
always@(*)begin
integer i;
for(i=0;i<3;i=i+1)begin
if(i==0)begin
{cout[i],sum[i]}=a[i]+b[i]+cin;
end else begin
{cout[i],sum[i]}=a[i]+b[i]+cout[i-1];
end
end
end
endmodule
● Adder (Exams/m2014 q4j)
module top_module (
input [3:0] x,
input [3:0] y,
output [4:0] sum);
wire [3:0]cout;
fa fa_inst[3:0](
.a(x),
.b(y),
.cin({cout[2:0],1'b0}),
.cout(cout[3:0]),
.sum(sum[3:0]));
assign sum[4]=cout[3];
endmodule
module fa(
input a,b,cin,
output cout,sum);
assign{cout,sum}=a+b+cin;
endmodule
注:当然也可以用generate-for循环写。
● Signed addition overflow (Exams/ece241 2014 q1c)
module top_module (
input [7:0] a,
input [7:0] b,
output [7:0] s,
output overflow
);
assign s=a+b;
assign overflow=(a[7]^b[7]==0 && s[7]==~a[7])?1'b1:1'b0;
endmodule
注:加法溢出只会发生在a、b同时为正和同时为负的情况,具体说,a、b同时为正而结果为负或者同时为负而结果为正时发生溢出。
● 100-bit binary adder (Adder100)
module top_module(
input [99:0] a, b,
input cin,
output cout,
output [99:0] sum );
assign{cout,sum}=a+b+cin;
endmodule
● 4-digit BCD adder (Bcdadd4)
module top_module (
input [15:0] a, b,
input cin,
output cout,
output [15:0] sum );
reg[3:0]cout_t;
/*//方法一:数组实例化
bcd_fadd bcd4[3:0](
.a(a[15:0]),
.b(b[15:0]),
.cin({cout_t[2:0],cin}),
.cout(cout_t[3:0]),
.sum(sum[15:0]));
assign cout=cout_t[3];*/
//方法二:循环例化generate-for
genvar i;
generate
for(i=0;i<4;i=i+1)begin:BCD4
if(i==0)begin
bcd_fadd bcd4(
.a(a[3:0]),
.b(b[3:0]),
.cin(cin),
.cout(cout_t[0]),
.sum(sum[3:0]));
end else begin
bcd_fadd bcd4(
.a(a[i*4+3-:4]),
.b(b[i*4+3-:4]),
.cin(cout_t[i-1]),
.cout(cout_t[i]),
.sum(sum[i*4+3-:4]));
end
end
endgenerate
assign cout=cout_t[3];
endmodule
3.1.4 Karnaugh Map to Circuit
● 3-variable (Kmap1)
module top_module(
input a,
input b,
input c,
output out );
/*//方法一:圈1
assign out=a|b|c;*/
//方法二:圈0
assign out=~((~a)&(~b)&(~c));
endmodule
注:卡诺图圈一得最简与或表达式,圈零得最简或与表达式。圈一时1为原变量,0为反变量,如11表示a&b。圈零时0为原变量,1为反变量,如00表示a|b。
● 4-variable (Kmap2)
module top_module(
input a,
input b,
input c,
input d,
output out );
/*//方法一:圈1
assign out=~b&~c | ~a&~d | a&c&d | b&c&d;*/
//方法二:圈0
assign out=(~a|~c|d) & (~a|~b|c) & (~b|~d|c) & (a|b|~c|~d);
endmodule
● 4-variable (Kmap3)
module top_module(
input a,
input b,
input c,
input d,
output out );
/*//方法一:圈一
assign out=a | ~b&c;*/
//方法二:圈0
assign out=(a|~b) & (c|~d) & (a|c);
endmodule
注:卡诺图中d表示x,也可以直接写作x,画圈时d(或x)可以根据“圈最大”原则当做0或1。
● 4-variable (Kmap4)
module top_module(
input a,
input b,
input c,
input d,
output out );
/*//方法一:以2/4列和1/3行当做一个整体;以1/3列和2/4行当做一个整体;
assign out=(a^b)&~(c^d) | ~(a^b)&(c^d);*/
//方法二:方法一基础上进一步简化
assign out=(a^b)^(c^d);
endmodule
● Minimum SOP and POS (Exams/ece241 2013 q2)
module top_module (
input a,
input b,
input c,
input d,
output out_sop,
output out_pos
);
/*//方法一:圈1,sop (sum of product)
assign out_sop=c&d | ~a&~b&c;*/
//方法二:圈0,pos (product of sum)
assign out_pos=c & (~a|d) & (~b|d);
endmodule
注:卡诺图如下:
● Karnaugh map (Exams/m2014 q3)
module top_module (
input [4:1] x,
output f );
/*//方法一:圈1,sop
assign f=~x[1]&x[3] | x[1]&x[2]&~x[3];*/
//方法二:圈0,pos
assign f=(x[1]|x[3]) & (~x[1]|x[2]) & (~x[1]|~x[3]);
endmodule
● Karnaugh map (Exams/2012 q1g)
module top_module (
input [4:1] x,
output f
);
/*//法一:SOP
assign f=x[3]&~x[1] | ~x[2]&~x[4] | x[2]&x[3]&x[4];*/
//法二:POS
assign f=(~x[2]|x[3]) & (x[3]|~x[4]) & (~x[1]|x[2]|~x[4]) & (~x[1]|~x[2]|x[4]);
endmodule
● K-map implemented with a multiplexer (Exams/ece241 2014 q3)
module top_module (
input c,
input d,
output [3:0] mux_in
);
/*//方法一:使用数选器,基于S&D | ~S&C
//二选一数选器实现或操作
assign mux_in[0]=d==1?d:c;
assign mux_in[1]=1'b0;
assign mux_in[2]=d==0?1'b1:1'b0;
//二选一数选器实现与操作
assign mux_in[3]=c==1?d:c;*/
//方法二:使用基础门(圈一)
assign mux_in[0]=c|d;
assign mux_in[1]=1'b0;
assign mux_in[2]=~d;
assign mux_in[3]=c&d;
endmodule
注:此题重点为如何用二选一数选器实现&和|的功能。考虑有一个数选器输出为~SA + SB,即assign out=(S==1)?B:A;。此时将信号A与S连接,得到AB;将信号B与S连接,得到~BA + B = ~BA + B + BA = A + B。
3.2 Sequential Logic
3.2.1 Latches and Flip-Flops
● D flip-flop (Dff)
module top_module (
input clk,
input d,
output reg q );
always@(posedge clk)begin
q<=d;
end
endmodule
● D flip-flops (Dff8)
module top_module (
input clk,
input [7:0] d,
output [7:0] q
);
always@(posedge clk)begin
q<=d;
end
endmodule
● DFF with reset (Dff8r)
module top_module (
input wire clk,
input wire reset,
input wire[7:0] d,
output reg[7:0] q
);
always@(posedge clk)begin
q<=reset==1?1'b0:d;
end
endmodule
注:同步复位(synchronous reset):复位信号不作为敏感列表always@(posedge clk);异步复位(asynchronous reset):复位信号作为敏感列表always@(posedge clk or posedge reset)。
● DFF with reset value (Dff8p)
module top_module (
input clk,
input reset,
input [7:0] d,
output [7:0] q
);
always@(negedge clk)begin
q<=reset==1?8'h34:d;
end
endmodule
● DFF with asynchronous reset (Dff8ar)
module top_module (
input clk,
input areset, // active high asynchronous reset
input [7:0] d,
output [7:0] q
);
always@(posedge clk or posedge areset)begin
q<=areset==1?8'd0:d;
end
endmodule
● DFF with byte enable (Dff16e)
module top_module (
input clk,
input resetn,
input [1:0] byteena,
input [15:0] d,
output [15:0] q
);
always@(posedge clk)begin
if(resetn==0)begin
q<=16'd0;
end else begin
case(byteena)
2'b10:q<={d[15:8],q[7:0]};
2'b01:q<={q[15:8],d[7:0]};
2'b11:q<={d[15:8],d[7:0]};
default:q<=q;
endcase
end
end
endmodule
注:byteena[1]控制输入数据d的高八位,byteena[0]控制输入数据d的低八位,未被控制部分保持输出。
● D Latch (Exams/m2014 q4a)
module top_module (
input d,
input ena,
output q);
always@(*)begin
q<=ena==1?d:q;
end
endmodule
注:1)锁存器是电平敏感,不是边沿敏感;2)锁存器虽然是电平敏感,但是是时序电路,用非阻塞赋值<=。
● DFF (Exams/m2014 q4b)
module top_module (
input clk,
input d,
input ar, // asynchronous reset
output q);
always@(posedge clk or posedge ar)begin
q<=ar==1?1'b0:d;
end
endmodule
● DFF (Exams/m2014 q4c)
module top_module (
input clk,
input d,
input r, // synchronous reset
output q);
always@(posedge clk)begin
q<=r==1?1'b0:d;
end
endmodule
● DFF+gate (Exams/m2014 q4d)
module top_module (
input clk,
input in,
output out);
always@(posedge clk)begin
out<=in^out;
end
endmodule
● Mux and DFF (Mt2015 muxdff)
module top_module (
input clk,
input L,
input r_in,
input q_in,
output reg Q);
always@(posedge clk)begin
Q<=L==1?r_in:q_in;
end
endmodule
注:此题仅需要描述一级电路即可,如图红框。
● Mux and DFF (Exams/2014 q4a)
module top_module (
input clk,
input w, R, E, L,
output Q
);
always@(posedge clk)begin
Q<=L==1?R:(E==1?w:Q);
end
endmodule
注:此题仅需要描述一级电路即可,如图红框。

● DFFs and gates (Exams/ece241 2014 q4)
module top_module (
input clk,
input x,
output z
);
wire q1,q2,q3;
always@(posedge clk)begin
q1 <= x^q1;
q2 <= x&~q2;
q3 <= x|~q3;
end
assign z = ~(q1|q2|q3);
endmodule
● Create circuit from truth table (Exams/ece241 2013 q7)
module top_module (
input clk,
input j,
input k,
output Q);
always@(posedge clk)begin
Q<=j&~k | ~(j|k)&Q | (j&k)&~Q;
end
endmodule
注:电路连接示意图:
● Detect an edge (Edgedetect)
module top_module (
input clk,
input [7:0] in,
output [7:0] pedge
);
integer i;
reg [7:0]in_copy;
always@(posedge clk)begin
in_copy<=in;
for(i=0;i<8;i=i+1)begin
pedge[i]<=(in[i]==1&&in_copy[i]==0)?1'b1:1'b0;
end
end
endmodule
注:输入信号的上升沿检测,in_copy用于保存前一时钟的输入值,in为当前时钟的输入值。非阻塞赋值。
● Detect both edges (Edgedetect2)
module top_module (
input clk,
input [7:0] in,
output [7:0] anyedge
);
integer i;
reg [7:0]in_copy;
always@(posedge clk)begin
in_copy<=in;
for(i=0;i<8;i=i+1)begin
anyedge[i]<=in[i]^in_copy[i]==1?1'b1:1'b0;
end
end
endmodule
注:输入信号的任意边沿检测。
● Edge capture register (Edgecapture)
module top_module (
input clk,
input reset,
input [31:0] in,
output [31:0] out
);
integer i;
reg [31:0]in_copy;
always@(posedge clk)begin
//in_copy<=in; //放在此处或最后一行均可
if(reset)begin
out<=0;
end else begin
for(i=0;i<32;i=i+1)begin
out[i]<=in[i]==0&&in_copy[i]==1?1'b1:out[i];
end
end
in_copy<=in; //放在第一行或此处均可,因为是非阻塞赋值
end
endmodule
注:带复位的输入信号下降沿检测。注意题目中"Capture" 表示检测到下降沿后一直为1,直到出现复位信号。in_copy<=in;放在always块的第一行或最后一行均可,因为是非阻塞赋值,只是放在最后一行更符合人的思维习惯。
● Dual-edge triggered flip-flop (Dualedge)
module top_module (
input clk,
input d,
output q
);
/*//法一:有毛刺
reg q1,q2;
always@(posedge clk)begin
q1<=d;
end
always@(negedge clk)begin
q2<=d;
end
assign q=clk==1?q1:q2;*/
//法二:无毛刺
reg q1,q2;
always@(posedge clk)begin
q1<=d^q2;
end
always@(negedge clk)begin
q2<=d^q1;
end
assign q=q1^q2;
endmodule
注:双边沿检测。always@(posedge clk or negedge clk)是不允许的。方法一利用时钟信号值判断上下沿,可能存在毛刺,参考博客;方法二巧妙利用了异或的特性,不存在毛刺,参考博客,上升沿时:q=q1^q2=(d^q2)^q2=d,下降沿时:q=q1^q2=q1^(d^q1)=d。
3.2.2 Counters
● Four-bit binary counter (Count15)
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);
always@(posedge clk)begin
q <= reset==1?4'd0:q+1'b1;
end
endmodule
● Decade counter (Count10)
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);
/*//方法一:三目运算符
always@(posedge clk)begin
q <= reset==1||q==4'd9 ? 4'd0 : q+1'b1;
end*/
//方法二:if-else结构
always@(posedge clk)begin
if(reset)begin
q <= 4'd0;
end else begin
if(q==4'd9)begin
q <= 4'd0;
end else begin
q <= q + 1'b1;
end
end
end
endmodule
注:0-9计数。
● Decade counter again (Count1to10)
module top_module (
input clk,
input reset,
output [3:0] q);
always@(posedge clk)begin
q<=reset==1||q==10?4'd1:q+1'b1;
end
endmodule
注:1-10计数。
● Slow decade counter (Countslow)
module top_module (
input clk,
input slowena,
input reset,
output [3:0] q);
always@(posedge clk)begin
if(reset)begin
q<=4'd0;
end else begin
if(slowena && q==9)begin
q<=4'd0;
end else if(slowena) begin
q<=q+1'b1;
end else begin
q<=q;
end
end
end
endmodule
注:带控制和复位的0-9计数。
● Counter 1-12 (Exams/ece241 2014 q7a)
module top_module (
input clk,
input reset,
input enable,
output [3:0] Q,
output c_enable,
output c_load,
output [3:0] c_d
);
always@(*)begin
if(reset)begin
c_d<=4'd1;
c_load<=1'b1;
end else begin
if(enable && Q==4'd12)begin
c_d<=4'd1;
c_load<=1'b1;
end else begin
c_d<=c_d; //此种写法有latch警告
//c_d<=1'b0; //也可赋0
c_load<=1'b0;
end
end
end
assign c_enable=enable;
count4 count4_inst (
.clk(clk),
.enable(c_enable),
.load(c_load),
.d(c_d),
.Q(Q));
endmodule
注:此题主要是要理解题目意图,题目的意思是用c_enable, c_load, c_d[3:0]信号去控制四位二进制计数器count4,c_enable控制count4的使能enable, c_load控制数据输入count4, c_d[3:0]控制输入count4的数据。参考博客。
● Counter 1000 (Exams/ece241 2014 q7b)
module top_module (
input clk,
input reset,
output OneHertz,
output [2:0] c_enable
);
wire [3:0]q0,q1,q2;
bcdcount counter0 (
.clk(clk),
.reset(reset),
.enable(c_enable[0]),
.Q(q0));
bcdcount counter1 (
.clk(clk),
.reset(reset),
.enable(c_enable[1]),
.Q(q1));
bcdcount counter2 (
.clk(clk),
.reset(reset),
.enable(c_enable[2]),
.Q(q2));
assign c_enable[0]=reset?1'b0:1'b1;
assign c_enable[1]=q0[3]&q0[0];
assign c_enable[2]=q1[3]&q1[0] & q0[3]&q0[0];
assign OneHertz = q2[3]&q2[0] & q1[3]&q1[0] & q0[3]&q0[0];
endmodule
注:每级计数器每计数十次向前进位一次。
● 4-digit decimal counter (Countbcd)
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:1] ena,
output [15:0] q);
//同步电路写法
wire [3:0] qcarry;
wire ena_4;
assign qcarry[0]=q[3]&q[0];
assign qcarry[1]=q[7]&q[4];
assign qcarry[2]=q[11]&q[8];
assign qcarry[3]=q[15]&q[12];
assign ena[1]=qcarry[0];
assign ena[2]=qcarry[0] & qcarry[1];
assign ena[3]=qcarry[0] & qcarry[1] & qcarry[2];
assign ena_4=qcarry[0] & qcarry[1] & qcarry[2] & qcarry[3];
always@(posedge clk)begin
if(reset | ena[1])begin
q[3:0]<=4'd0;
end else begin
q[3:0]<=q[3:0]+1'b1;
end
end
always@(posedge clk)begin
if(reset | ena[2])begin
q[7:4]<=4'd0;
end else if(ena[1])begin
q[7:4]<=q[7:4]+1'b1;
end else begin
q[7:4]<=q[7:4];
end
end
always@(posedge clk)begin
if(reset | ena[3])begin
q[11:8]<=4'd0;
end else if(ena[2])begin
q[11:8]<=q[11:8]+1'b1;
end else begin
q[11:8]<=q[11:8];
end
end
always@(posedge clk)begin
if(reset | ena_4)begin
q[15:12]<=4'd0;
end else if(ena[3])begin
q[15:12]<=q[15:12]+1'b1;
end else begin
q[15:12]<=q[15:12];
end
end
endmodule
注:此处采用同步连接的方式,应该也可以采用异步连接方式(此处未给出,有兴趣可以思考讨论)。我用异步连接方式描述该电路时遇到的问题:1、复位时异步电路立即复位导致验证不通过;2、为了解决异步复位的问题,使用异步复位同步释放结构,随之而来的新问题是整个电路输出延后了两拍(周期)。异步复位同步释放参考博客。下图为同步连接电路图(字丑见谅)。

● 12-hour clock (Count clock)
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);
//同步电路写法
wire hh_l,hh_h,mm_l,mm_h,ss_l,ss_h,hh_pm;
assign ss_l=ss[3]&ss[0]; //每xx:xx:x9
assign ss_h=ss[6]&ss[4] & ss_l; //每xx:xx:59
assign mm_l=mm[3]&mm[0] & ss_h; //每xx:x9:59
assign mm_h=mm[6]&mm[4] & mm_l; //每xx:59:59
assign hh_l=hh[3]&hh[0] & mm_h; //每x9:59:59
assign hh_h=hh[4]&hh[1] & mm_h; //每12:59:59
assign hh_pm=hh[4]&hh[0] & mm_h; //每11:59:59
always@(posedge clk)begin
if(reset)begin
ss<=8'h00;
end else if(ena)begin
if(ss_h)begin
ss<=8'h00; //每59秒准备清零
end else if(ss_l)begin
ss<={ss[7:4]+1'b1,4'h0}; //每9秒准备进位
end else begin
ss<={ss[7:4],ss[3:0]+1'b1}; //每秒准备加一
end
end else begin
ss<=ss;
end
end//秒
always@(posedge clk)begin
if(reset)begin
mm<=8'h00;
end else if(ena)begin
if(mm_h)begin
mm<=8'h00; //每59分59秒准备清零
end else if(mm_l)begin
mm<={mm[7:4]+1'b1,4'h0}; //每9分59秒准备进位
end else if(ss_h)begin
mm<={mm[7:4],mm[3:0]+1'b1}; //每59秒准备加一分
end else begin
mm<=mm;
end
end else begin
mm<=mm;
end
end//分
always@(posedge clk)begin
if(reset)begin
hh<=8'h12;
end else if(ena)begin
if(hh_h)begin
hh<=8'h01; //每12时59分59秒时准备“清零”
end else if(hh_l)begin
hh<={hh[7:4]+1'b1,4'h0}; //每9时59分59秒准备进位
end else if(mm_h)begin
hh<={hh[7:4],hh[3:0]+1'b1}; //每59分59秒加一时
end else begin
hh<=hh;
end
end else begin
hh<=hh;
end
end//时
always@(posedge clk)begin
if(reset)begin
pm<=1'b0;
end else if(ena)begin
if(hh_pm)begin
pm<=~pm; //每11时59分59秒PM指示翻转
end else begin
pm<=pm;
end
end else begin
pm<=pm;
end
end//PM指示
endmodule
注:准确把握何时复位、进位即可,同样采用的是同步连接方式。问题:可以用异步连接实现吗?
3.2.3 Shift Registers
● 4-bit shift register (Shift4)
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)begin
q<=4'd0;
end else if(load)begin
q<=data;
end else if(ena)begin
q<=q>>1;
end else begin
q<=q;
end
end
endmodule
● Left/right rotator (Rotate100)
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)begin
q<=data;
end else begin
case(ena)
2'b00:q<=q;
2'b01:q<={q[0],q[99:1]};
2'b10:q<={q[98:0],q[99]};
2'b11:q<=q;
default:q<=q;
endcase
end
end
endmodule
● Left/right arithmetic shift by 1 or 8 (Shift18)
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)begin
q<=data;
end else if(ena)begin
case(amount)
2'b00:q<={q[63-1:0],{1{1'b0}}}; //左移,与逻辑左移相同
2'b01:q<={q[63-8:0],{8{1'b0}}};
2'b10:q<={q[63],{1{q[63]}},q[62:0+1]}; //右移,符号位不变,空余位补符号位
2'b11:q<={q[63],{8{q[63]}},q[62:0+8]};
default:q<=q;
endcase
end else begin
q<=q;
end
end
endmodule
注:算术右移方法:符号位不变,右移n位空出来的位用符号位补充。例如:1000_1101右移2位为1110_0011。
● 5-bit LFSR (Lfsr5)
module top_module(
input clk,
input reset, // Active-high synchronous reset to 5'h1
output [4:0] q
);
always@(posedge clk)begin
if(reset)begin
q<=5'd1;
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
注:线性反馈移位寄存器(Linear Feedback Shift Register, LFSR)的反馈函数为异或网络,参与异或的位称为抽头(tap)。n位(级)的LFSR共产生2^n-1个状态,不包含的状态为全零态。
● 3-bit LFSR (Mt2015 lfsr)
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
LEDR[0]<=KEY[1]==1?SW[0]:LEDR[2];
LEDR[1]<=KEY[1]==1?SW[1]:LEDR[0];
LEDR[2]<=KEY[1]==1?SW[2]:LEDR[1]^LEDR[2];
end
endmodule
● 32-bit LFSR (Lfsr32)
module top_module(
input clk,
input reset, // Active-high synchronous reset to 32'h1
output [31:0] q
);
integer i;
always@(posedge clk)begin
if(reset)begin
q<=32'd1;
end else begin
for(i=0;i<32;i=i+1)begin
if(i==31)begin
q[i]<=q[0]^1'b0;
end else if(i==0 || i==1 || i==21) begin
q[i]<=q[i+1]^q[0];
end else begin
q[i]<=q[i+1];
end
end
end
end
endmodule
注:线性反馈移位寄存器的反馈从最左端(MSB)输入。问题:为什么以下写法是错误的?错误信息:索引32超出范围。
///错误写法///
module top_module(
input clk,
input reset, // Active-high synchronous reset to 32'h1
output [31:0] q
);
reg [7:0]i;
always@(posedge clk)begin
if(reset)begin
q<=32'd1;
end else begin
for(i=8'd0;i<8'd32;i=i+1'd1)begin
case(i)
8'd0 :q[i]<=q[i+1]^q[0];
8'd1 :q[i]<=q[i+1]^q[0];
8'd21:q[i]<=q[i+1]^q[0];
8'd31:q[i]<=q[0]^1'b0;
default:q[i]<=q[i+1];
endcase
end
end
end
endmodule
///错误写法///
● Shift register (Exams/m2014 q4k)
module top_module (
input clk,
input resetn, // synchronous reset
input in,
output out);
wire [2:0]q;
always@(posedge clk)begin
if(!resetn)begin
out<=1'b0;
q<=3'd0;
end else begin
q[2]<=in;
q[1]<=q[2];
q[0]<=q[1];
out<=q[0];
end
end
endmodule
● Shift register (Exams/2014 q4b)
module top_module (
input [3:0] SW,
input [3:0] KEY,
output [3:0] LEDR
);
/*//方法一:generate-for结构
genvar i;
generate
for(i=0;i<4;i=i+1)begin:ShiftReg4
if(i==3)begin
MUXDFF MUXDFF_inst(
.clk(KEY[0]),
.E(KEY[1]),
.L(KEY[2]),
.w(KEY[3]),
.R(SW[i]),
.Q(LEDR[i]));
end else begin
MUXDFF MUXDFF_inst(
.clk(KEY[0]),
.E(KEY[1]),
.L(KEY[2]),
.w(LEDR[i+1]),
.R(SW[i]),
.Q(LEDR[i]));
end
end
endgenerate*/
//方法二:数组实例化
MUXDFF MUXDFF_inst[3:0](
.clk({4{KEY[0]}}),
.E({4{KEY[1]}}),
.L({4{KEY[2]}}),
.w({KEY[3],LEDR[3:1]}),
.R(SW),
.Q(LEDR));
endmodule
module MUXDFF (
input w,E,R,L,clk,
output Q);
always@(posedge clk)begin
Q<=L==1?R:(E==1?w:Q);
end
endmodule
● 3-input LUT (Exams/ece241 2013 q12)
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)begin
Q[7]<=Q[6];
Q[6]<=Q[5];
Q[5]<=Q[4];
Q[4]<=Q[3];
Q[3]<=Q[2];
Q[2]<=Q[1];
Q[1]<=Q[0];
Q[0]<=S;
end else begin
Q<=Q;
end
end
always@(*)begin
case({A,B,C})
3'd0:Z=Q[0];
3'd1:Z=Q[1];
3'd2:Z=Q[2];
3'd3:Z=Q[3];
3'd4:Z=Q[4];
3'd5:Z=Q[5];
3'd6:Z=Q[6];
3'd7:Z=Q[7];
endcase
end
endmodule
3.2.4 More Circuits
● Rule 90 (Rule90)
module top_module(
input clk,
input load,
input [511:0] data,
output [511:0] q );
integer i;
always@(posedge clk)begin
if(load)begin
q<=data;
end else begin
for(i=0;i<512;i=i+1)begin
if(i==0)begin
q[i]<=1'b0^q[i+1];
end else if(i==511)begin
q[i]<=q[i-1]^1'b0;
end else begin
q[i]<=q[i-1]^q[i+1];
end
end
end
end
endmodule
注:每位的下一状态为左右相邻位的异或,边界位q[-1]、q[512]为0。
● Rule 110 (Rule110)
module top_module(
input clk,
input load,
input [511:0] data,
output [511:0] q
);
integer i; //left(A):i+1,center(B):i,right(C):i-1
always@(posedge clk)begin
if(load)begin
q<=data;
end else begin
for(i=0;i<512;i=i+1)begin
if(i==0)begin
q[i]<=(q[i]^1'b0) | (~q[i+1]&1'b0);
end else if(i==511)begin
q[i]<=(q[i]^q[i-1]) | (~1'b0&q[i-1]);
end else begin
q[i]<=(q[i]^q[i-1]) | (~q[i+1]&q[i-1]); //B^C + ~A&B 或者 B^C + ~A&C
end
end
end
end
endmodule
注:从真值表画出卡诺图,逻辑表达式为B^C + !A&B 或者 B^C + !A&C。left(A):i+1;center(B):i;right(C):i-1。

● Conway’s Game of Life 16x16 (Conwaylife)
module top_module(
input clk,
input load,
input [255:0] data,
output [255:0] q );
integer i;
reg [3:0]sum;
always@(posedge clk)begin
if(load)begin
q<=data;
end else begin
for(i=0;i<256;i=i+1)begin
if(i==0 || i==15 || i==240 || i==255)begin //四个角
if(i==0)begin
sum=q[i+255]+q[i+1]+q[i+241]+q[i+240]+q[i+31]+q[i+15]+q[i+16]+q[i+17]; //0
end else if(i==15)begin
sum=q[i-1]+q[i+1]+q[i-15]+q[i+240]+q[i+239]+q[i+15]+q[i+16]+q[i+225]; //15
end else if(i==240)begin
sum=q[i-1]+q[i+1]+q[i-15]+q[i-16]+q[i-225]+q[i+15]+q[i-240]+q[i-239]; //240
end else if(i==255)begin
sum=q[i-1]+q[i-255]+q[i-15]+q[i-16]+q[i-17]+q[i-241]+q[i-240]+q[i-31]; //255
end
end else if((0<i && i<15) || (240<i && i<255) || (i%16==15) || (i%16==0))begin //四条边
if(0<i && i<15)begin
sum=q[i-1]+q[i+1]+q[i+239]+q[i+240]+q[i+241]+q[i+15]+q[i+16]+q[i+17]; //上边
end else if(240<i && i<255) begin
sum=q[i-1]+q[i+1]+q[i-15]+q[i-16]+q[i-17]+q[i-239]+q[i-240]+q[i-241]; //下边
end else if(i%16==15) begin
sum=q[i-1]+q[i-15]+q[i-31]+q[i-16]+q[i-17]+q[i+15]+q[i+16]+q[i+1]; //左边
end else if(i%16==0) begin
sum=q[i+15]+q[i+1]+q[i-15]+q[i-16]+q[i-1]+q[i+31]+q[i+16]+q[i+17]; //右边
end
end else begin //中间
sum=q[i-1]+q[i+1]+q[i-15]+q[i-16]+q[i-17]+q[i+15]+q[i+16]+q[i+17];
end
case(sum)
4'd2:q[i]<=q[i];
4'd3:q[i]<=1'b1;
default:q[i]<=1'b0;
endcase
end
end
end
endmodule
注:16×16的矩阵,计算每个元素周围8个元素的和,并以此判断下一状态。黄底红字为边界元素应该补齐的位,绿底为四个角的元素,橙底代表除四个角以外的边界上的元素,蓝底代表中间的元素。上面时序电路中用了阻塞赋值,不推荐此种写法,单纯做题。 此题也可以用SV写,但我不会。

3.2.5 Finite State Machines
● Simple FSM 1 (asynchronous reset) (Fsm1)
module top_module(
input clk,
input areset, // Asynchronous reset to state B
input in,
output out);
parameter A=1'b0, B=1'b1;
reg state, next_state;
always @(posedge clk or posedge areset) begin
// 状态转移
if(areset)begin
state<=B;
end else begin
state<=next_state;
end
end
always @(*) begin
// 状态转移逻辑
case({in,state})
{1'b0,A}:next_state=B;
{1'b0,B}:next_state=A;
{1'b1,A}:next_state=A;
{1'b1,B}:next_state=B;
default:next_state=next_state;
endcase
end
// 输出
assign out = state;
endmodule
注:三段式状态机,第一段写状态的转移(时序电路);第二段写具体的状态转移逻辑(组合电路);第三段写状态的输出(可时序可组合)。
● Simple FSM 1 (synchronous reset) (Fsm1s)
// Note the Verilog-1995 module declaration syntax here:
module top_module(clk, reset, in, out);
input clk;
input reset; // Synchronous reset to state B
input in;
output out;
reg out;
parameter A=1'b0, B=1'b1;
reg present_state, next_state;
//状态转移
always @(posedge clk) begin
if (reset) begin
present_state <= B;
end else begin
present_state <= next_state;
end
end
//状态转移逻辑
always @(*) begin
case( {in,present_state} )
{1'b0,A}: next_state <= B;
{1'b0,B}: next_state <= A;
{1'b1,A}: next_state <= A;
{1'b1,B}: next_state <= B;
default: next_state <= present_state;
endcase
end
//状态输出
always @(*) begin
out = present_state;
end
endmodule
注:同步复位T触发器。
● Simple FSM 2 (asynchronous reset) (Fsm2)
module top_module(
input clk,
input areset, // Asynchronous reset to OFF
input j,
input k,
output out);
parameter OFF=1'b0, ON=1'b1;
reg state, next_state;
//状态转移
always @(posedge clk or posedge areset) begin
if(areset)begin
state <= OFF;
end else begin
state <= next_state;
end
end
//状态转移逻辑
always @(*) begin
if(j==1 && state==OFF) begin
next_state = ON; //OFF→ON
end else if(k==1 && state==ON) begin
next_state = OFF; //ON→OFF
end else begin
next_state = state; //状态不变
end
end
//状态输出
always @(*) begin
out = state;
end
endmodule
注:异步复位JK触发器。
● Simple FSM 2 (synchronous reset) (Fsm2s)
module top_module(
input clk,
input reset, // Synchronous reset to OFF
input j,
input k,
output out);
parameter OFF=1'b0, ON=1'b1;
reg state, next_state;
//状态转移
always @(posedge clk) begin
if(reset)begin
state <= OFF;
end else begin
state <= next_state;
end
end
//状态转移逻辑
always @(*) begin
case(state)
ON:begin
if(k==1)begin
next_state = OFF;
end else begin
next_state = ON;
end
end
OFF:begin
if(j==1)begin
next_state = ON;
end else begin
next_state = OFF;
end
end
endcase
end
//状态输出
assign out = state;
endmodule
注:同步复位JK触发器。
● Simple state transitions 3 (Fsm3comb)
module top_module(
input in,
input [1:0] state,
output [1:0] next_state,
output out);
parameter A=2'b00, B=2'b01, C=2'b10, D=2'b11;
//状态转移逻辑
always@(*)begin
if(in==0)begin
case(state)
A: next_state = A;
B: next_state = C;
C: next_state = A;
D: next_state = C;
default:next_state = state;
endcase
end else begin
case(state)
A: next_state = B;
B: next_state = B;
C: next_state = D;
D: next_state = B;
default:next_state = state;
endcase
end
end
//状态输出
always@(*)begin
case(state)
A: out = 1'b0;
B: out = 1'b0;
C: out = 1'b0;
D: out = 1'b1;
default:out = out;
endcase
end
endmodule
● Simple one-hot state transitions 3 (Fsm3onehot)
module top_module(
input in,
input [3:0] state,
output [3:0] next_state,
output out);
parameter A=0, B=1, C=2, D=3;
//状态转移逻辑:采用独热码,写出下一状态每个位的表达式
assign next_state[A] = state[A]&(~in) | state[C]&(~in);
assign next_state[B] = state[A]&in | state[B]&in | state[D]∈
assign next_state[C] = state[B]&(~in) | state[D]&(~in);
assign next_state[D] = state[C]∈
//状态输出
assign out = state[D];
endmodule
注:独热码编码描述状态。
● Simple FSM 3 (asynchronous reset) (Fsm3)
module top_module(
input clk,
input in,
input areset,
output out);
/*//方法一:非独热码编码,四个state分别为:A=2'd0,B=2'd1,C=2'd2,D=2'd3
reg [1:0]state, next_state;
parameter A=2'd0, B=2'd1, C=2'd2, D=2'd3;
//状态转移
always@(posedge clk or posedge areset)begin
if(areset)begin
state <= A;
end else begin
state <= next_state;
end
end
//状态转移逻辑
always@(*)begin
if(in==0)begin
case(state)
A: next_state = A;
B: next_state = C;
C: next_state = A;
D: next_state = C;
default: next_state = state; //保持当前状态不变
endcase
end else begin
case(state)
A: next_state = B;
B: next_state = B;
C: next_state = D;
D: next_state = B;
default: next_state = state; //保持当前状态不变
endcase
end
end
//状态输出
always@(*)begin
case(state)
A: out = 1'b0;
B: out = 1'b0;
C: out = 1'b0;
D: out = 1'b1;
default: out = out; //保持当前状态不变
endcase
end*/
//方法二:独热码编码,四个state分别为:4'b0001,4'b0010,4'b0100,4'b1000
reg [3:0]state,next_state;
parameter A=4'd0, B=4'd1, C=4'd2, D=4'd3;
//状态转移
always@(posedge clk or posedge areset)begin
if(areset)begin
state <= 4'b0001; //注意复位到state的第一个状态4'b0001
end else begin
state <= next_state;
end
end
//状态转移逻辑
always@(*)begin
next_state[A] = state[A]&(~in) | state[C]&(~in);
next_state[B] = state[A]&in | state[B]&in | state[D]∈
next_state[C] = state[B]&(~in) | state[D]&(~in);
next_state[D] = state[C]∈
end
//状态输出
always@(*)begin
out = state[D];
end
endmodule
注:采用方法二时注意复位到state的第一个状态为4'b0001,定义的A,B,C,D用于表示位而非状态。
● Simple FSM 3 (synchronous reset) (Fsm3s)
module top_module(
input clk,
input in,
input reset,
output out);
/*// 方法一:不采用独热码编码,四个状态为:A(2'd0),B(2'd1),C(2'd2),D(2'd3)
parameter A=2'd0, B=2'd1, C=2'd2, D=2'd3;
reg [1:0]state, next_state;
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= A;
end else begin
state <= next_state;
end
end
// 状态转移逻辑
always@(*)begin
if(~in)begin
case(state)
A: next_state = A;
B: next_state = C;
C: next_state = A;
D: next_state = C;
default: next_state = state;
endcase
end else begin
case(state)
A: next_state = B;
B: next_state = B;
C: next_state = D;
D: next_state = B;
default: next_state = state;
endcase
end
end
// 状态输出
always@(*)begin
out = (state==D) ? 1'b1 : 1'b0;
end*/
// 方法二:独热码编码,四个状态为:A(4'b0001),B(4'b0010),C(4'b0100),D(4'b1000)
parameter A=2'd0, B=2'd1, C=2'd2, D=2'd3;
reg [3:0]state, next_state;
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= 4'b0001;
end else begin
state <= next_state;
end
end
// 状态转移逻辑
always@(*)begin
next_state[A] = state[A]&(~in) | state[C]&(~in) ;
next_state[B] = state[A]&in | state[B]&in | state[D]&in ;
next_state[C] = state[B]&(~in) | state[D]&(~in) ;
next_state[D] = state[C]&in ;
end
// 状态输出
always@(*)begin
out = state[D];
end
endmodule
注:采用方法二时注意复位到state的第一个状态为4'b0001,定义的A,B,C,D用于表示位而非状态。
● Design a Moore FSM (Exams/ece241 2013 q4)
module top_module (
input clk,
input reset,
input [3:1] s,
output fr3,
output fr2,
output fr1,
output dfr
);
parameter A=2'd0, B=2'd1, C=2'd2, D=2'd3;
reg [1:0]curr_state, next_state;
reg [2:0]fr_pre;
always@(posedge clk)begin
fr_pre <= {fr3,fr2,fr1}; // 记录前一时刻的流量阀开关情况,流量阀侧面反应了水位,流量阀开得多表示水位低
end
// 状态转移
always@(posedge clk)begin
if(reset)begin
curr_state <= A;
end else begin
curr_state <= next_state;
end
end
// 状态转移逻辑
always@(*)begin
casez(s)
3'b000: next_state = A; //没有传感器“工作”
3'b001: next_state = B; //传感器S1“工作”
3'b01z: next_state = C; //传感器S2“工作”,此时S1肯定也在“工作”
3'b1zz: next_state = D; //传感器S3“工作”,此时S1、S2肯定也在“工作”
default: next_state = 3'hx;
endcase
end
// 状态输出
always@(*)begin
if(curr_state==A)begin
dfr = 1'b1;
end else if(curr_state==D)begin
dfr = 1'b0;
end else begin
if( {fr3,fr2,fr1}>fr_pre )begin
dfr = 1'b1; // 上一时刻水位高于当前时刻
end else if( {fr3,fr2,fr1}<fr_pre )begin
dfr = 1'b0; // 上一时刻水位低于当前时刻
end else begin
dfr = dfr; // 上一时刻水位等于当前时刻
end
end
case(curr_state)
A: {fr3, fr2, fr1} = {1'b1, 1'b1, 1'b1};
B: {fr3, fr2, fr1} = {1'b0, 1'b1, 1'b1};
C: {fr3, fr2, fr1} = {1'b0, 1'b0, 1'b1};
D: {fr3, fr2, fr1} = {1'b0, 1'b0, 1'b0};
default: {fr3, fr2, fr1} = 3'hx;
endcase
end
endmodule
注:此题主要在于dfr的判断,dfr只有在复位和上一时刻水位高于当前时刻时才为1。流量阀fr3, fr2, fr1的开(1)关(0)情况可以侧面反应水位,流量阀开得多表示水位低。我的解法报了锁存warning,可以通过HDLBits网页的Solution查看作者的解法
● Lemmings 1 (Lemmings1)
module top_module(
input clk,
input areset, // 复位向左走
input bump_left,
input bump_right,
output walk_left,
output walk_right);
parameter LEFT=1'd0, RIGHT=1'd1;
reg state, next_state;
// 状态转移
always@(posedge clk or posedge areset)begin
if(areset)begin
state <= LEFT;
end else begin
state <= next_state;
end
end
// 状态转移逻辑
always@(*)begin
if(state==LEFT)begin
casez( {bump_left,bump_right} )
2'b1z: next_state = RIGHT;
default: next_state = LEFT;
endcase
end else begin
casez( {bump_left,bump_right} )
2'bz1: next_state = LEFT;
default: next_state = RIGHT;
endcase
end
end
// 状态输出
always@(*)begin
walk_left = state==LEFT ? 1'b1 : 1'b0;
walk_right = state==RIGHT ? 1'b1 : 1'b0;
end
endmodule
注:画出状态转移图即可。

● Lemmings 2 (Lemmings2)
module top_module(
input clk,
input areset, // 复位向左走
input bump_left,
input bump_right,
input ground,
output walk_left,
output walk_right,
output aaah );
parameter left=2'd0, right=2'd1, fall_l=2'd2, fall_r=2'd3;
reg [1:0]state, next, temp;
// 状态转移
always@(posedge clk or posedge areset)begin
if(areset)begin
state <= left;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
left:
if(ground)begin
next = bump_left ? right : left;
end else begin
next = fall_l;
end
right:
if(ground)begin
next = bump_right ? left : right;
end else begin
next = fall_r;
end
fall_l: next = ground==1 ? left : fall_l;
fall_r: next = ground==1 ? right : fall_r;
default: next = 2'hx;
endcase
end
// 状态输出
always@(*)begin
case(state)
left: {walk_left, walk_right, aaah} = {1'b1, 1'b0, 1'b0};
right: {walk_left, walk_right, aaah} = {1'b0, 1'b1, 1'b0};
fall_l: {walk_left, walk_right, aaah} = {1'b0, 1'b0, 1'b1};
fall_r: {walk_left, walk_right, aaah} = {1'b0, 1'b0, 1'b1};
default: {walk_left, walk_right, aaah} = 3'hx;
endcase
end
endmodule
注:状态转移图(字丑见谅):

● Lemmings 3 (Lemmings3)
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
input ground,
input dig,
output walk_left,
output walk_right,
output aaah,
output digging );
parameter left=3'd0, right=3'd1, fall_l=3'd2, fall_r=3'd3, dig_l=3'd4, dig_r=3'd5;
reg [2:0]state, next;
// 状态转移
always@(posedge clk or posedge areset)begin
if(areset)begin
state <= left;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
left:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_l;
4'b11zz: next = dig_l;
4'b101z: next = right;
4'b100z: next = left;
default: next = state;
endcase
right:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_r;
4'b11zz: next = dig_r;
4'b10z1: next = left;
4'b10z0: next = right;
default: next = state;
endcase
fall_l:
casez({ground,dig,bump_left,bump_right})
4'b1zzz: next = left;
default: next = state;
endcase
fall_r:
casez({ground,dig,bump_left,bump_right})
4'b1zzz: next = right;
default: next = state;
endcase
dig_l:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_l;
default: next = state;
endcase
dig_r:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_r;
default: next = state;
endcase
default:next = 3'hx;
endcase
end
// 状态输出
always@(*)begin
case(state)
left: {walk_left,walk_right,aaah,digging} = 4'b1000;
right: {walk_left,walk_right,aaah,digging} = 4'b0100;
fall_l: {walk_left,walk_right,aaah,digging} = 4'b0010;
fall_r: {walk_left,walk_right,aaah,digging} = 4'b0010;
dig_l: {walk_left,walk_right,aaah,digging} = 4'b0001;
dig_r: {walk_left,walk_right,aaah,digging} = 4'b0001;
default:{walk_left,walk_right,aaah,digging} = 4'hx;
endcase
end
endmodule
注:状态转移图:

● Lemmings 4 (Lemmings4)
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
input ground,
input dig,
output walk_left,
output walk_right,
output aaah,
output digging );
parameter left=3'd0, right=3'd1, fall_l=3'd2, fall_r=3'd3, dig_l=3'd4, dig_r=3'd5, splatter=3'd6;
reg [2:0]state, next;
reg [4:0]cnt;
/*// 方法一:用 fall_l和fall_r 两种状态做掉落时间判断条件(当前状态)
always@(posedge clk)begin
if((state==fall_l) || (state==fall_r))begin
cnt <= cnt==5'd20 ? cnt : cnt+1'd1; // 方法一:cnt==5'd20
end else begin
cnt <= 5'd0;
end
end*/
/*// 方法二:用输入信号 ground 做掉落时间判断条件
always@(posedge clk)begin
if(ground==0)begin
cnt <= cnt==5'd21 ? cnt : cnt+1'd1; // 方法二:cnt==5'd21
end else begin
cnt <= 5'd0;
end
end*/
// 方法三:用 fall_l和fall_r 两种状态做掉落时间判断条件(下一状态)
always@(posedge clk)begin
if((next==fall_l) || (next==fall_r))begin
cnt <= cnt==5'd21 ? cnt : cnt+1'd1; // 方法三:cnt==5'd21
end else begin
cnt <= 5'd0;
end
end
// 状态转移
always@(posedge clk or posedge areset)begin
if(areset)begin
state <= left;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
left:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_l;
4'b11zz: next = dig_l;
4'b101z: next = right;
4'b100z: next = left;
default: next = state;
endcase
right:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_r;
4'b11zz: next = dig_r;
4'b10z1: next = left;
4'b10z0: next = right;
default: next = state;
endcase
fall_l:
if(cnt==21 && ground==1)begin // 方法一:cnt==5'd20;方法二:cnt==5'd21;方法三:cnt==5'd21
next = splatter;
end else begin
casez({ground,dig,bump_left,bump_right})
4'b1zzz: next = left;
default: next = state;
endcase
end
fall_r:
if(cnt==21 && ground==1)begin // 方法一:cnt==5'd20;方法二:cnt==5'd21;方法三:cnt==5'd21
next = splatter;
end else begin
casez({ground,dig,bump_left,bump_right})
4'b1zzz: next = right;
default: next = state;
endcase
end
dig_l:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_l;
default: next = state;
endcase
dig_r:
casez({ground,dig,bump_left,bump_right})
4'b0zzz: next = fall_r;
default: next = state;
endcase
splatter: next = state;
default:next = 3'hx;
endcase
end
// 状态输出
always@(*)begin
case(state)
left: {walk_left,walk_right,aaah,digging} = 4'b1000;
right: {walk_left,walk_right,aaah,digging} = 4'b0100;
fall_l: {walk_left,walk_right,aaah,digging} = 4'b0010;
fall_r: {walk_left,walk_right,aaah,digging} = 4'b0010;
dig_l: {walk_left,walk_right,aaah,digging} = 4'b0001;
dig_r: {walk_left,walk_right,aaah,digging} = 4'b0001;
splatter: {walk_left,walk_right,aaah,digging} = 4'b0000;
default: {walk_left,walk_right,aaah,digging} = 4'hx;
endcase
end
endmodule
注: 方法一(当前状态做判断条件)和方法二(输入做判断条件)、方法三(下一状态做判断条件)计数cnt不同的原因是因为:方法一利用状态机第一段(状态转移)中的变量,该always块是一个时序电路,对其内部的变量的赋值要在块结束后才会执行(即需要延迟一拍,非阻塞赋值);而方法二、三是利用状态机第二段(状态转移逻辑)中的变量,该always块是一个组合电路,对其内部的变量的赋值立即执行(阻塞赋值),因此方法一比方法二、三少计数一次。状态转移图:

● One-hot FSM (Fsm onehot)
module top_module(
input in,
input [9:0] state,
output [9:0] next_state,
output out1,
output out2);
parameter s0=0, s1=1, s2=2, s3=3, s4=4, s5=5, s6=6, s7=7, s8=8, s9=9;
// 状态转移逻辑
always@(*)begin
next_state[s0] = ~in & (state[s0] | state[s1] | state[s2] | state[s3] | state[s4] | state[s7] | state[s8] | state[s9]);
next_state[s1] = in & (state[s0] | state[s8] | state[s9]);
next_state[s2] = in & state[s1];
next_state[s3] = in & state[s2];
next_state[s4] = in & state[s3];
next_state[s5] = in & state[s4];
next_state[s6] = in & state[s5];
next_state[s7] = in & (state[s6] | state[s7]);
next_state[s8] = ~in & state[s5];
next_state[s9] = ~in & state[s6];
end
// 状态输出
always@(*)begin
out1 = state[s8] | state[s9];
out2 = state[s7] | state[s9];
end
endmodule
● PS/2 packet parser (Fsm ps2)
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output done);
parameter byte1=2'd0, byte2=2'd1, byte3=2'd2, finish=2'd3;
reg [1:0] state, next;
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= byte1;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
byte1: next = in[3]==1 ? byte2 : byte1;
byte2: next = byte3;
byte3: next = finish;
finish: next = in[3]==1 ? byte2 : byte1;
default: next = 2'hx;
endcase
end
// 状态输出
always@(*)begin
case(state)
byte1: done = 1'b0;
byte2: done = 1'b0;
byte3: done = 1'b0;
finish: done = 1'b1;
default: done = 1'hx;
endcase
end
endmodule
注:此题不能用3个状态机进行描述的原因:我的理解是3个状态机不能描述输入in[3]=1与完成done=1同时发生的状态,即第一个波形图所描述情形。状态转移图:

● PS/2 packet parser and datapath (Fsm ps2data)
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output [23:0] out_bytes,
output done);
parameter byte1=2'd0, byte2=2'd1, byte3=2'd2, finish=2'd3;
reg [1:0] state, next;
reg [23:0] out_mem;
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= byte1;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
byte1: next = in[3]==1 ? byte2 : byte1;
byte2: next = byte3;
byte3: next = finish;
finish: next = in[3]==1 ? byte2 : byte1;
default:next = 2'hx;
endcase
end
// 状态输出
always@(*)begin
case(state)
byte1: done = 1'b0;
byte2: done = 1'b0;
byte3: done = 1'b0;
finish: done = 1'b1;
default:done = 1'hx;
endcase
end
// 保存datapath
always@(posedge clk)begin
case(state)
byte1: out_mem[23:16] <= in;
byte2: out_mem[15:8] <= in;
byte3: out_mem[7:0] <= in;
finish: out_mem[23:16] <= in[3]==1 ? in : 8'd0;
default:out_mem = 24'hx;
endcase
end
assign out_bytes = state==finish ? out_mem : 24'hx;
endmodule
● Serial receiver (Fsm serial)
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
/*// 方法一:将中间态data0-data7分别描述
parameter data0=4'd0,data1=4'd1, data2=4'd2, data3=4'd3, data4=4'd4, data5=4'd5, data6=4'd6, data7=4'd7;
parameter idle=4'd8, start=4'd9, stop=4'd10, error=4'd11;
reg [3:0] state, next;
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= idle;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
idle: next = in==0 ? start : idle;
start: next = data0;
data0: next = data1;
data1: next = data2;
data2: next = data3;
data3: next = data4;
data4: next = data5;
data5: next = data6;
data6: next = data7;
data7: next = in==1 ? stop : error;
error: next = in==1 ? idle : state;
stop: next = in==0 ? start : idle;
default:next = 4'hx;
endcase
end
// 状态输出
assign done = state==stop ? 1'b1 : 1'b0;*/
// 方法二:将中间态data0-data7用计数器描述
parameter idle=3'd0, start=3'd1, data=3'd2, stop=3'd3, error=3'd4;
reg [2:0] state, next;
reg [3:0] cnt;
// data计数
always@(posedge clk)begin
cnt <= state==data ? cnt+1'b1 : 4'd0; // 可改为 cnt <= next==data ? cnt+1'b1 : 4'd0;
end
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= idle;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
idle: next = in==0 ? start : idle;
start: next = data;
data:
if(cnt==7)begin // 计数用 cnt <= next==data ? cnt+1'b1 : 4'd0; 时,对应 cnt==8
next = in==1 ? stop : error;
end else begin
next = state;
end
error: next = in==1 ? idle : state;
stop: next = in==0 ? start : idle;
default:next = 4'hx;
endcase
end
// 状态输出
assign done = state==stop ? 1'b1 : 1'b0;
endmodule
注:状态转移图:

● Serial receiver and datapath (Fsm serialdata)
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
);
/*// 方法一:将中间态data0-data7分别描述
parameter data0=4'd0,data1=4'd1, data2=4'd2, data3=4'd3, data4=4'd4, data5=4'd5, data6=4'd6, data7=4'd7;
parameter idle=4'd8, start=4'd9, stop=4'd10, error=4'd11;
reg [3:0] state, next;
reg [7:0] out_mem;
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= idle;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
idle: next = in==0 ? start : idle;
start: next = data0;
data0: next = data1;
data1: next = data2;
data2: next = data3;
data3: next = data4;
data4: next = data5;
data5: next = data6;
data6: next = data7;
data7: next = in==1 ? stop : error;
error: next = in==1 ? idle : state;
stop: next = in==0 ? start : idle;
default:next = 4'hx;
endcase
end
// data保存
always@(*)begin
case(next)
data0: out_mem[0] = in;
data1: out_mem[1] = in;
data2: out_mem[2] = in;
data3: out_mem[3] = in;
data4: out_mem[4] = in;
data5: out_mem[5] = in;
data6: out_mem[6] = in;
data7: out_mem[7] = in;
default:out_mem = out_mem;
endcase
end
// 状态输出
assign done = state==stop ? 1'b1 : 1'b0;
assign out_byte = state==stop ? out_mem : 8'hx;*/
/*// 方法二:将中间态data0-data7用计数器描述(利用下一状态)
parameter idle=3'd0, start=3'd1, data=3'd2, stop=3'd3, error=3'd4;
reg [2:0] state, next;
reg [3:0] cnt;
reg [7:0] out_mem;
// data计数
always@(posedge clk)begin
cnt <= next==data ? cnt+1'b1 : 4'd0;
end
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= idle;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
idle: next = in==0 ? start : idle;
start: next = data;
data:
if(cnt==8)begin
next = in==1 ? stop : error;
end else begin
next = state;
end
error: next = in==1 ? idle : state;
stop: next = in==0 ? start : idle;
default:next = 4'hx;
endcase
end
// data保存
always@(posedge clk)begin
if(next==data)begin
out_mem[cnt] <= in;
end else begin
out_mem <= out_mem;
end
end
// 状态输出
assign done = state==stop ? 1'b1 : 1'b0;
assign out_byte = state==stop ? out_mem : 8'd0;*/
// 方法三:将中间态data0-data7用计数器描述(利用当前状态)
parameter idle=3'd0, start=3'd1, data=3'd2, stop=3'd3, error=3'd4;
reg [2:0] state, next;
reg [3:0] cnt;
reg [7:0] out_mem;
// data计数
always@(posedge clk)begin
cnt <= state==data ? cnt+1'b1 : 4'd0;
end
// 状态转移
always@(posedge clk)begin
if(reset)begin
state <= idle;
end else begin
state <= next;
end
end
// 状态转移逻辑
always@(*)begin
case(state)
idle: next = in==0 ? start : idle;
start: next = data;
data:
if(cnt==7)begin
next = in==1 ? stop : error;
end else begin
next = state;
end
error: next = in==1 ? idle : state;
stop: next = in==0 ? start : idle;
default:next = 4'hx;
endcase
end
// data保存
always@(posedge clk)begin
if(state==start || state==data)begin
if(state==start)begin
out_mem[0] <= in;
end else begin
out_mem[cnt+1] <= in;
end
end else begin
out_mem <= out_mem;
end
end
// 状态输出
assign done = state==stop ? 1'b1 : 1'b0;
assign out_byte = state==stop ? out_mem : 8'd0;
endmodule
● Serial receiver with parity checking (Fsm serialdp)
module top_module(
input wire clk,
input wire in,
input wire reset,
output reg [7:0] out_byte,
output reg done
);
localparam IDLE=3'd0, START=3'd1, DATA=3'd2, PARITY=3'd3, DONE=3'd4, ERROR=3'd5;
reg [2:0] curr, next;
reg [3:0] cnt;
reg [7:0] out_t;
wire odd, rst_par;
reg par;
// State transitions
always@(posedge clk)begin
if(reset)begin
curr <= IDLE;
end else begin
curr <= next;
end
end
// State logic
always@(*)begin
case(curr)
IDLE : next = in==0 ? START : curr;
START : next = DATA;
DATA : next = cnt==8 ? PARITY : curr;
PARITY : next = in==1 ? DONE : ERROR;
DONE : next = in==0 ? START : IDLE;
ERROR : next = in==1 ? IDLE : curr;
default : next = IDLE;
endcase
end
// Data counter and save in
always@(posedge clk)begin
cnt <= next==DATA ? cnt+1'b1 : 4'h0;
out_t[cnt] <= next==DATA ? in : out_t[cnt];
end
// State output
// 方法一:利用当前状态对输出进行判断
// 获取奇偶校验结果
always@(posedge clk)begin
par <= curr==PARITY ? odd : par;
end
// State output
always@(*)begin
out_byte = (curr==DONE && par==1) ? out_t : 8'hx;
done = (curr==DONE && par==1) ? 1'b1 : 1'b0;
end
/*// 方法二:利用下一状态对输出进行判断
always@(posedge clk)begin
out_byte <= (next==DONE && odd==1) ? out_t : 8'hx;
done <= (next==DONE && odd==1) ? 1'b1 : 1'b0;
end*/
// Parity checking
assign rst_par = next==START;
parity u_parity(
.clk(clk),
.reset(rst_par),
.in(in),
.odd(odd));
endmodule
注:状态转移图如下。为了更好理解方法一可以查看下方波形图。


开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)