1. (2.4.6)优先译码器
每个输入端有着不同的重要性,只要更重要的输入端输入了有效信号,我们就不再考虑来自其他次重要输入端的输入信号。
优先译码器真值表:
输入第一个1出现哪个位置输出几
module top_module (
input [3:0] in,
output reg [1:0] pos);
always @(*) begin // Combinational always block
case (in)
4'h0: pos = 2'h0; // I like hexadecimal because it saves typing.
4'h1: pos = 2'h0;
4'h2: pos = 2'h1;
4'h3: pos = 2'h0;
4'h4: pos = 2'h2;
4'h5: pos = 2'h0;
4'h6: pos = 2'h1;
4'h7: pos = 2'h0;
4'h8: pos = 2'h3;
4'h9: pos = 2'h0;
4'ha: pos = 2'h1;
4'hb: pos = 2'h0;
4'hc: pos = 2'h2;
4'hd: pos = 2'h0;
4'he: pos = 2'h1;
4'hf: pos = 2'h0;
default: pos = 2'b0;
endcase
end
endmodule
2.(2.4.7)casez
casez语句表明设定为z的位数可以不关心,只关心设定为1、0的位。
3.(2.5.1)三目选择运算符
错误代码:定义x,y不要忘记位宽
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);//
// assign intermediate_result1 = compare? true: false;
wire x;
wire y;
assign x = (a>b) ? b : a;
assign y = (c>x) ? x : c;
assign min = (d>y) ? y : d;
endmodule
4.(2.5.2)递减运算符
对一个向量内的位进行操作:
5.(2.5.4)for循环
不要忘记begin-end
integer i;
always@(*)begin
for(i = 0;i < 100;i = i + 1 )begin
out [i] = in[99-i];
end
end
6.(2.5.5)generate
(1)行波进位加法器ripple-carry adder
N-bit加法器可以根据1-bit全加器组合而成。每个全加器的输出进位cout作为下一个全加器的输入进位cin
(2)generate语句
generate语句的最主要功能就是对module、reg、assign、always、task等语句或者模块进行复制。
generate_for语句:
(a)、必须使用genvar申明一个正整数变量,用作for循环的判断。(genvar是generate语句中的一种变量类型,用在generate_for中声明正整数变量。)
(b)、需要复制的语句必须写到begin_end语句里面。就算只有一句!!!!!!
(c)、begin_end需要有一个类似于模块名的名字。
例:使用generate例化100个全加器或者做100次全加器的运算,需要注意第一次比较特殊,第一次的进位来自输入,而不是前一位的进位,所以需要分开写。
module top_module(
input [99:0] a, b,
input cin,
output [99:0] cout,
output [99:0] sum );
generate
genvar i;
for(i=0;i<=99;i=i+1)begin:full_adder
if(i==0)begin
assign {cout[0],sum[0]} = a[0] + b[0] + cin;
end
else begin
assign {cout[i],sum[i]} = a[i] + b[i] + cout[i-1];
end
end
endgenerate
endmodule
7.(2.5.6)使用generate语句例化
使用generate例化100个BCD adder,需要注意第一次比较特殊,第一次的进位来自输入,而不是前一位的进位,所以需要分开写。
不要忘记最后输出进位。
module top_module(
input [399:0] a, b,
input cin,
output cout,
output [399:0] sum );
wire [99:0] cout_to_cin;
generate
genvar i;
for(i=0;i<100;i=i+1)begin:BCD_ADDER
if(i==0)begin
bcd_fadd bcd_fadd_inst(
.a (a[3:0]),
.b (b[3:0]),
.cin (cin),
.cout (cout_to_cin[0]),
.sum (sum[3:0])
);
end
else begin
bcd_fadd bcd_fadd_inst(
.a (a[ 4*i+3 : 4*i] ),
.b (b[ 4*i+3 : 4*i] ),
.cin (cout_to_cin[i-1] ),
.cout (cout_to_cin[i] ),
.sum (sum[ 4*i+3 : 4*i] )
);
end
end
assign cout = cout_to_cin[99];
endgenerate
endmodule
8.(3.1.17)100位输入向量与相邻位数关系
out_both:每一位都与左边一位进行与操作;如果是最高位,则不进行。
out_any:每一位都与右边一位进行或操作;如果是最低位,则不进行。
out_different:每一位都与左边一位进行异或操作;如果是最高位,则与最低位进行异或。
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[99:1] & in[98:0];
assign out_any = in[99:1] | in[98:0];
assign out_different = {in[0],in[99:1]} ^ in;
endmodule
9.(3.1.2.4) 256-to-1 选择器
创建一个 1 位宽、256:1 的多路复用器。256 个输入全部打包到一个 256 位输入向量中。sel=0 应选择 in[0],sel=1 选择位 in[1],sel=2 选择位 in[2],依此类推。
module top_module(
input [255:0] in,
input [7:0] sel,
output out );
assign out = in[sel];
endmodule
10. (3.1.2.5)4位宽 256-to-1 4-bit 多路选择器
创建一个 4 位宽、256:1 的多路复用器。256 个 4 位输入全部封装到单个 1024 位输入向量中。sel=0 应选择位 in[3:0],sel=1 选择位 in[7:4],sel=2 选择位 in[11:8],依此类推。
用向量部分选择处理
module top_module(
input [1023:0] in,
input [7:0] sel,
output [3:0] out );
assign out = in[sel *4 +: 4];
endmodule
(1). 向量名 [起始位+:位宽]:从起始位开始(包括起始位), 宽度为位宽,从起始位递增到位宽的一段向量区域。
如:
wire [31:0] a; a[0 +: 8] 等效为 a[7 : 0]; // 起始位 在最右侧
wire [0:31] b; b[0 +: 8] 等效为 b[0 : 7]; // 起始位 在最左侧
(2).向量名 [起始位-:位宽]:从起始位开始(包括起始位), 宽度为位宽,从起始位递减到位宽的一段向量区域。如:
wire [31 : 0] a; a[7-: 8] 等效为 a[7 : 0]; // 起始位 在最左侧
wire [0 : 31] b; b[7-: 8] 等效为 b[0 : 7]; // 起始位 在最右侧
(3).起始位可以是变量,但位宽必须是常量
11. (3.1.3.5)补码相加溢出
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] & ~s[7]) | (~a[7] & ~b[7] & s[7]);
endmodule
12. (3.1.4.4)同或异或卡诺图化简
红色:a、b的同或 与上 c、d的异或 ;
蓝色:a、b的异或与上 c、d的同或 。
13. (3.1.4.5)最大项最小项
具有四个输入(a、b、c、d)的单输出数字系统在输入端出现 2、7 或 15 时生成逻辑 1,当 0、1、4、5、6、9、10、13 或 14 出现时生成逻辑 0。数字 3、8、11 和 12 的输入条件在此系统中从未出现。
SOP 积之和 圈1和doncare项(最小项)
pos 和之积 圈0和doncare项 之后 整体取反
14. (3.1.4.8)使用多路复用器实现的K-map
当输入cd为00时,则需要ab为10输出才为1,所以应该使mux_in[0]=1,其他情况同理。
module top_module (
input c,
input d,
output [3:0] mux_in
);
assign mux_in[0] = (~c & d)|(c & d)|(c & ~d);
assign mux_in[1] =1'b0;
assign mux_in[2] = (~c & ~d)|(c & ~d);
assign mux_in[3] = c & d;
endmodule
15. (3.2.1.6)16D触发器
仅需要修改部分触发器中的值。字节使能信号控制当前时钟周期中16个寄存器中哪个字节需被修改。byteena[1]控制高字节d[15:8],而byteena[0]控制低字节d[7:0]。
\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)
q <= 16'd0;
else
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;
default:;
endcase
end
endmodule
16. (3.2.1.16)双边边沿检测
module top_module (
input clk,
input [7:0] in,
output [7:0] anyedge
);
wire [7:0]in_r;
always@(posedge clk)begin
in_r <= in;
anyedge = in ^ in_r;
end
endmodule
17. (3.2.1.17)边沿捕获
对于32位矢量中的每个位,当输入信号在一个时钟周期内从1变为下一个时钟周期的0时,就可以捕获。"捕获 "意味着输出将保持1,直到寄存器被复位(同步复位)。每个输出位的行为就像一个SR触发器:在1到0转换发生后的周期,输出位应该被设置(到1)。当复位为高电平时,输出位应在正的时钟边沿复位(至0)。
输入为32位的数据,捕获每一位的下降沿,如果检测到下降沿,输出中对应的位置高且保持直到检测到reset为高;如果某一个时钟上升沿同时检测到输入数据位的下降沿和reset为高,则reset优先,即输出为低。
module top_module (
input clk,
input reset,
input [31:0] in,
output [31:0] out
);
reg [31:0] r_in;
always @ (posedge clk)
begin
r_in <= in;
if(reset)
out <= 0;
else
for(int i = 0; i <=31; i ++)
begin
if(~in[i] & r_in[i])
out[i] <= 1;
else
out[i] <= out[i];
end
end
endmodule
或者
reg [31:0]in_r;
always @(posedge clk )begin
in_r <= in;
end
always @(posedge clk )begin
if(reset)
out <= 'd0;
else
out <= ~in & in_r |out;
end
18. (3.2.1.18)双边边沿触发器
首先 always@(posedge clk or negedge clk)这种语句肯定是不能综合的;其次也不能用两个always块分别在时钟上升沿、时钟下降沿进行操作,这涉及到多驱动问题。
比较合适的方法是设计两个中间变量,分别在时钟上升沿、时钟下降沿进行赋值,最后使用组合逻辑输出。
module top_module (
input clk,
input d,
output q
);
reg q1, q2;
assign q = clk ? q1 : q2;
always@(posedge clk)
begin
q1 <= d;
end
always@(negedge clk)
begin
q2 <= d;
end
endmodule
或者 任何一个数异或一个数再异或同一个数,将得到本身
module top_module (
input clk,
input d,
output q
);
reg p,n;
always@(posedge clk)begin
p <= d ^ n; //q = p ^ n = d ^ n ^ n = d
end
always@(negedge clk)begin
n <= d ^ p; //q = p ^ n = p ^ d ^ p = d
end
assign q = p ^ n;
endmodule
19. (3.2.2.5)1-12计数器
例化count4模块
module count4(
input clk,
input enable,
input load,
input [3:0] d,
output reg [3:0] Q
);
使能信号enable:直接把c_enable连到这里就可以
载入信号load:结合c_load可以实现复位清零,以及最大值清零
载入值c_load:同上
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 ,Q );
assign c_enable = enable;
assign c_load = reset|((Q=='d12)&&(enable==1'b1));
assign c_d = c_load ? 4'd1 :4'dx;
endmodule
20. (3.2.2.6)计数器1000
设计一个周期为1000的计数器.题目给出了一个4位的BCD模块,例化三次这个模块,第一次例化计数10次(每个时钟周期+1),第二次例化计数10次(在第一次例化每计数10次时加+1),第三次例化计数10次(在第二次例化每计数10次时加+1),这样就实现了每1000个时钟周期+1,对应的输出信号的频率也就变成了1hz。
module top_module (
input clk,
input reset,
output OneHertz,
output [2:0] c_enable
); //
wire [3:0]one,ten,hund;//记得加位宽
bcdcount counter0 (clk, reset, c_enable[0],one);
bcdcount counter1 (clk, reset, c_enable[1],ten);
bcdcount counter2 (clk, reset, c_enable[2],hund);
assign c_enable = {one =='d9&&ten=='d9,one=='d9,1'b1};
// c_enable[0]=1'b1:个位计数;c_enable[1]=(one=='d9):十位计数;c_enable[2]=(one=='d9&&ten=='d9):百位计数;
assign OneHertz = (one=='d9 && ten=='d9 && hund=='d9);
endmodule
21. (3.2.2.7)4位BCD计数器
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:1] ena,
output [15:0] q);
wire en0,en1,en2,en3;
assign en0 = 1'b1;
assign en1 = (q[3:0]=='d9);
assign en2 = (q[3:0]=='d9 && q[7:4]=='d9);
assign en3 = (q[3:0]=='d9 && q[7:4]=='d9 && q[11:8]=='d9);
assign ena = {q[3:0]=='d9&&q[7:4]=='d9&&q[11:8]=='d9, q[7:4]=='d9&&q[3:0]=='d9, q[3:0]=='d9};
BCDcounter BCDcounter0(
.clk (clk),
.reset (reset),
.ena (en0),
.q (q[3:0])
);
BCDcounter BCDcounter1(
.clk (clk),
.reset (reset),
.ena (en1),
.q (q[7:4])
);
BCDcounter BCDcounter2(
.clk (clk),
.reset (reset),
.ena (en2),
.q (q[11:8])
);
BCDcounter BCDcounter3(
.clk (clk),
.reset (reset),
.ena (en3),
.q (q[15:12])
);
endmodule
module BCDcounter(
input clk,
input reset,
input ena,
output [3:0]q
);
always@(posedge clk)begin
if(reset)
q <= 'd0;
else if(ena)begin
if(q == 'd9)
q<= 'd0;
else
q <= q + 1'b1;
end
end
endmodule
22. (3.2.2.8)12小时制计数器
有复位信号,且复位的优先级高于使能端,复位信号有效时能复位到12点,其中pm 表示 AM 为 0,PM 为 1。
要求实现的时间是1点到12点。
出现错误:直接计数到59输出的是3c,使用BCD计数器正确。
计数时使用十进制计数器无法启动,改成十六进制正确。
pm翻转时,时分秒分别是11、59、59。
module top_module(
input clk,
input reset,
input ena,
output pm,
output [7:0] hh,
output [7:0] mm,
output [7:0] ss);
reg pm1;
always@(posedge clk)begin
if(reset)
pm1 <= 'h0;
else if((hh=='h11)&(mm=='h59)&(ss=='h59))
pm1 <= ~pm1;
end
assign pm = pm1;
BCD1 BCD1(
.clk (clk),
.reset (reset),
.ena (ena&(mm=='h59)&(ss=='h59)),//有使能信号且秒分都记满则时开始计数
.hh (hh)
);
BCD2 BCD2_1(
.clk (clk),
.reset (reset),
.ena (ena&&(ss=='h59)),//有使能信号且秒记满分开始计数
.ms (mm)
);
BCD2 BCD2_2(
.clk (clk),
.reset (reset),
.ena (ena), //有使能信号,秒开始计数
.ms (ss)
);
endmodule
//12计数器
module BCD1(
input clk,
input reset,
input ena,
output [7:0]hh
);
always@(posedge clk)begin
if(reset)
hh <= 'h12;
else if(ena)begin
if(hh == 'h12)
hh <= 'd1;
else if(hh[3:0]=='h9)begin
hh[3:0] <= 'd0;
hh[7:4] <= hh[7:4] + 1'b1;
end
else
hh[3:0] <= hh[3:0] + 1'b1;
end
end
endmodule
//60计数器
module BCD2(
input clk,
input reset,
input ena,
output [7:0]ms
);
always@(posedge clk)begin
if(reset)
ms <= 'd0;
else if(ena)begin
if(ms == 'h59)
ms <= 'd0;
else if(ms[3:0]=='h9)begin
ms[3:0] <= 'd0;
ms[7:4] <= ms[7:4] + 1'b1;
end
else
ms[3:0] <= ms[3:0] + 1'b1;
end
end
endmodule
23. (3.2.3.1)4位移位寄存器
load:加载带有 data[3:0] 的移位寄存器,而不是移位。
ena:向右移动(q[3] 变为零,q[0] 移出并消失)。
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 <= 'd0;
else if(load)
q <= data;
else if(ena)
q <= q >> 1'b1;
else
q <= q;
end
endmodule
24. (3.2.3.3)算术移位寄存器
算数左移:和逻辑左移一样。空出来的位用0补;
算数右移:算术移位,也就是包含符号位的移位,对于正数来说,最高位为0,对于负数来说,最高位为1,所以进行算术移位时,如果是左移,那不用管符号位的问题,如果是右移,就要将符号位补在高位。比如:5位数字11000算术右移1为11100,而逻辑右移将产生01100;5位数字01000算术右移1等于00100,而逻辑右移将产生相同的结果,因为原始数字是非负的。
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 begin
if(ena)
case(amount)
2'b00: q <= q<<1;
2'b01: q <= q<<8;
2'b10:
q<= {q[63],q[63:1]};
2'b11:
q <= {{8{q[63]}},q[63:8]};
endcase
end
end
endmodule
25. (3.2.3.4)线性反馈移位寄存器
线性反馈移位寄存器(linear feedback shift register, LFSR)是指,给定前一状态的输出,将该输出的线性函数再用作输入的移位寄存器。异或运算是最常见的单比特线性函数:对寄存器的某些位进行异或操作后作为输入,再对寄存器中的各比特进行整体移位(百度百科定义)。
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];
q[3] <= q[4];
q[2] <= q[3]^q[0];
q[1] <= q[2];
q[0] <= q[1];
end
end
endmodule
26. (3.2.3.9)三输入LUT
用8个D触发器创建一个8位移位寄存器。标记D触发器的输出分别为Q[0]到Q[7]。移位寄存器输入称为S,输入Q[0] (MSB先移位)。使能输入enable控制是否移位,扩展电路使其有3个额外的输入A,B,C和一个输出Z。电路的行为应该如下:当ABC为000时,Z=Q[0],当ABC为001时,Z=Q[1],以此类推。
即输入的时候是个移位寄存器,输出是根据“地址”来“随机读取
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
27. (3.2.4.1)规则90
在每个时间步中,每个单元的下一个状态是单元的两个当前邻居的异或。
在这个电路中,创建一个512单元系统(q(511:0)),并在每个时钟周期中前进一个时间步长。加载(load)表明系统的状态应该加载data[511:0]至q中,假设边界(q[0]和q[512])都为零。
这个只要用输入q左移1位异或上输入q右移1位即可
module top_module(
input clk,
input load,
input [511:0] data,
output [511:0] q );
always @(posedge clk)begin
if(load)
q[511:0] <= data[511:0];
else
q <= (q<<1'b1)^(q>>1'b1);
end
endmodule
28. (3.2.4.3)康威的生命游戏
“游戏”是在二维单元格上进行的,其中每个单元格要么是 1(活)要么是 0(死)。在每个时间步长中,每个单元格都会根据其拥有的邻居数量而改变状态:
0-1 邻居:单元格变为 0。
2 个邻居:单元状态不变。
3 个邻居:单元格变为 1。
4+ 邻居:单元格变为 0。
使用 16x16 的环形线圈,其中两侧环绕到网格的另一侧。例如,角单元格 (0,0) 有 8 个相邻单元:(15,1)、(15,0)、(15,15)、(0,1)、(0,15)、(1,1)、(1,0) 和 (1,15)。
module top_module(
input clk,
input load,
input [255:0] data,
output [255:0] q );
reg [3:0]cout;
integer i;
always@(posedge clk)begin
if(load)
q <= data;
else
for(i=0;i<256;i=i+1)begin
if(i==0)
cout = q[1]+q[16]+q[17]+q[15]+q[31]+q[255]+q[240]+q[241];
else if(i==15)
cout = q[14]+q[16]+q[30]+q[31]+q[254]+q[255]+q[240]+q[0];
else if(i==240)
cout = q[1]+q[15]+q[255]+q[241]+q[239]+q[224]+q[225]+q[0];
else if(i==255)
cout = q[15]+q[14]+q[238]+q[224]+q[254]+q[240]+q[239]+q[0];
else if(i>0&i<15)
cout = q[i-1]+q[i+1]+q[i+239]+q[i+240]+q[i+241]+q[i+15]+q[i+16]+q[i+17];
else if(i>240&i<255)
cout = q[i-1]+q[i+1]+q[i-239]+q[i-240]+q[i-241]+q[i-15]+q[i-16]+q[i-17];
else if(i%16==0)
cout = q[i+1]+q[i+15]+q[i+16]+q[i+17]+q[i+31]+q[i-16]+q[i-15]+q[i-1];
else if(i%16==15)
cout = q[i-1]+q[i-15]+q[i-16]+q[i-17]+q[i-31]+q[i+15]+q[i+16]+q[i+1];
else
cout = q[i-1]+q[i+1]+q[i-16]+q[i-17]+q[i-15]+q[i+15]+q[i+16]+q[i+17];
case (cout)
4'd2: q[i] <= q[i];
4'd3: q[i] <= 1'b1;
default: q[i] <= 1'b0;
endcase
end
end
endmodule
29. (3.2.5.5)独热码
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[D]&in) | (state[B]&in);
assign next_state[C] = (state[B]&~in) | (state[D]&~in);
assign next_state[D] = (state[C]&in);
//组合逻辑实现输出
assign out = (state[D]);
endmodule
30. (3.2.5.9)摩尔FSM
根据水位变化情况输出不同状态,有三个传感器S1,S2,S3,可以将水库分成四部分,低于S1时,输出FR1,FR2,FR3;水位介于S1
S2时,输出输出FR1,FR2;水位介于S2S3时,输出输出FR1;水位高于S3时,可以认为FR1,FR2,FR3全为零;同时还会将上一刻水位状态和现在的水位状态进行比较,如果高于现在水位,需要打开(三角)FR,否则不需要打开。
设计4个状态,状态的变化应该都发生在临近状态之间:
S0:水位低于S1。当检测到S1传感器有效时,说明水位超过了S1,状态跳转到S1;其他情况则保持水位不变。
S1:水位在S1~S2。当检测到S2传感器有效时,说明水位超过了S2,状态跳转到S2;当检测到S1传感器无效时,说明水位在S1之下,状态跳转到S0;其他情况则保持水位不变。
S2:水位在S2~S3。当检测到S3传感器有效时,说明水位超过了S3,状态跳转到S3;当检测到S2传感器无效时,说明水位在S2之下,状态跳转到S1;其他情况则保持水位不变。
S3:水位超过S3。当检测到S3传感器无效时,说明水位在S3之下,状态跳转到S2;其他情况则保持水位不变。
输出FR1、FR2、FR3根据所在状态对照上图进行输出即可。
dfr的输出原则是水位发生了下降则dfr输出1,所以需要比较次态和现态的关系。举例,若现态为S1,次态为S0,则说明水位下降,dfr输出1;若现态为S1,次态为S2,则说明水位上升,dfr输出0;若现态为S1,次态为S1,则说明水位不变,dfr输出不变(dfr <= dfr)。
module top_module(
input clk , //输入时钟
input reset , //高电平有效的复位信号
input [3:1] s , //输入信号
output fr3 , //输出信号
output fr2 , //输出信号
output fr1 , //输出信号
output dfr //输出信号
);
//------------<状态机参数定义>------------------------------------------
//使用独热码进行状态定义
parameter S0 = 4'b0001 , //低于S1
S1 = 4'b0010 , //S1~S2
S2 = 4'b0100 , //S2~S3
S3 = 4'b1000 ; //超过S3
//------------<reg定义>-------------------------------------------------
reg [3:0] cur_state , //定义现态寄存器
next_state ; //定义次态寄存器
//三段式状态机第一段:同步时序描述状态转移
always @(posedge clk) begin
if(reset)
cur_state <= S0;
else
cur_state <= next_state;
end
//三段式状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
always @(*) begin
if(reset)
next_state = S0;
else
case(cur_state)
S0:
if(s == 3'b001) //检测到S1传感器,代表水位上升,水位在S1-S2
next_state = S1;
else //水位无变化
next_state = S0;
S1:
if(s == 3'b011) //检测到S1、S2传感器,代表水位上升,水位超过了S2-S3
next_state = S2;
else if(s == 3'b000) //没有检测到传感器,代表水位下降,水位在S1之下
next_state = S0;
else //水位无变化
next_state = S1;
S2:
if(s == 3'b111) //检测到S1、S2、S3传感器,代表水位上升,水位超过了S3
next_state = S3;
else if(s == 3'b001) //检测到S1传感器,代表水位下降,水位在S1-S2
next_state = S1;
else //水位无变化
next_state = S2;
S3:
if(s == 3'b011) //检测到S1、S2传感器,代表水位下降,水位超过了S2-S3
next_state = S2;
else //水位无变化
next_state = S3;
default:next_state = S0;
endcase
end
//三段式状态机第三段:时序逻辑描述输出
always @(posedge clk) begin
if(reset)begin
fr1 <= 1'b1;
fr2 <= 1'b1;
fr3 <= 1'b1;
dfr <= 1'b1;
end
else
case(next_state)
S0:begin
fr1 <= 1'b1;
fr2 <= 1'b1;
fr3 <= 1'b1;
dfr <= 1'b1;
end
S1:begin
fr1 <= 1'b1;
fr2 <= 1'b1;
fr3 <= 1'b0;
if (cur_state == S0) //上一个状态是S0,说明水位升高了
dfr <= 1'b0; //不打开开关
else if (cur_state == S1) //上一个状态是S1,说明水位没变化
dfr <= dfr; //开关保持不变
else //上一个状态是S2,说明水位降低了
dfr <= 1'b1; //打开开关
end
S2:begin
fr1 <= 1'b1;
fr2 <= 1'b0;
fr3 <= 1'b0;
if (cur_state == S1) //上一个状态是S1,说明水位升高了
dfr <= 1'b0; //不打开开关
else if (cur_state == S2) //上一个状态是S2,说明水位没变化
dfr <= dfr; //开关保持不变
else //上一个状态是S3,说明水位降低了
dfr <= 1'b1; //打开开关
end
S3:begin
fr1 <= 1'b0;
fr2 <= 1'b0;
fr3 <= 1'b0;
if (cur_state == S2) //上一个状态是S2,说明水位升高了
dfr <= 1'b0; //不打开开关
else //上一个状态是S3,说明水位没变化
dfr <= dfr; //开关保持不变
end
default:begin
fr1 <= 1'b1;
fr2 <= 1'b1;
fr3 <= 1'b1;
dfr <= 1'b1;
end
endcase
end
endmodule
31. (3.2.5.12)lemming3
当旅鼠处于地面(ground为1)走动时,一旦dig为1则旅鼠处于挖掘状态,digging输出1;直到ground为0则旅鼠处于掉落状态(aaah为1)
fall 的优先级高于 dig,而 dig 的优先级高于切换方向的优先级。
32. (3.2.5.14)FSM onehot
module top_module(
input in,
input [9:0] state,
output [9:0] next_state,
output out1,
output out2);
parameter s0=4'd0,
s1=4'd1,
s2=4'd2,
s3=4'd3,
s4=4'd4,
s5=4'd5,
s6=4'd6,
s7=4'd7,
s8=4'd8,
s9=4'd9;
assign next_state[s0] = ~in&(state[s0]|state[s1]|state[s2]|state[s3]|state[s4]|state[s7]|state[s8]|state[s9]);
assign next_state[s1] = in&(state[s0]|state[s8]|state[s9]);
assign next_state[s2] = in&state[s1];
assign next_state[s3] = in&state[s2];
assign next_state[s4] = in&state[s3];
assign next_state[s5] = in&state[s4];
assign next_state[s6] = in&state[s5];
assign next_state[s7] = in&(state[s6]|state[s7]);
assign next_state[s8] = ~in&(state[s5]);
assign next_state[s9] = ~in&(state[s6]);
assign out1 = state[s8]|state[s9];
assign out2 = state[s7]|state[s9];
endmodule
33. (3.2.5.15)FSM ps2
第一个状态D1判断接受数据流的起始字节,如果是起始字节,则紧接着的两个字节为数据流的一部分,数据流总共三个字节,接受完毕之后的下一个时钟周期,输出接受完毕信号Done,于此同时,判断是否接受到了下一个数据流的起始字节,如果是则继续接受下两个字节,否则进入状态D1,继续判断是否接受到起始字节。
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output done); //
reg [3:0]state,n_state;
parameter s0=4'b0001,
s1=4'b0010,
s2=4'b0100,
s3=4'b1000;
// State transition logic (combinational)
always@(posedge clk)begin
if(reset)
state <= s0;
else
state <= n_state;
end
// State flip-flops (sequential)
always@(*)begin
if(reset)
n_state = s0;
else case(state)
s0:
if(in[3])
n_state = s1;
else
n_state = s0;
s1:
n_state = s2;
s2:
n_state = s3;
s3:
if(in[3])
n_state = s1;
else
n_state = s0;
default:;
endcase
end
// Output logic
always@(posedge clk)begin
if(reset)
done <= 1'b0;
else case(n_state)
s3: done <= 1'b1;
default:done <= 1'b0;
endcase
end
endmodule
34. (3.2.5.17)serial receiver
IDLE: 初始状态,检测到输入in为高电平后跳转到DATA状态,否则保持该状态
DATA: 在这个状态有个从0开始的计数器cnt,当计数器计数到7时,代表接收到了8-bit数据,计数到8时输入信号若为1,则代表该信号为终止信号,一次传输结束,跳转到状态STOP;若输入信号为0,则代表传输错误,跳转到状态ERROR,继续等待终止信号的到来;若计数器小于8,则保持该状态(传输未结束)
ERROR: 检测到输入in为0后代表传输依然错误,保持在该状态,继续检测终止信号;检测到输入in为1后代表传输结束,由于该次传输出现了错误,所以直接跳转到初始状态
STOP: 检测到输入in为0后代表检测到了一个新的起始信号,接下来就可以接收数据,所以跳转到状态DATA;若检测到输入in为1(空闲状态)则跳转到IDLE状态
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
reg [3:0]cnt;
reg [3:0]state,n_state;
parameter IDEL= 4'b0001,
DATA = 4'b0010,
ERROR = 4'b0100,
STOP = 4'b1000;
always@(posedge clk)begin
if(reset)
state <= IDEL;
else
state <= n_state;
end
always@(*)begin
if(reset)
n_state = IDEL;
else case(state)
IDEL:
if(!in)
n_state = DATA;
else
n_state = IDEL;
DATA:
if(cnt == 4'd8)begin
if(in)
n_state = STOP;
else
n_state = ERROR;
end
else
n_state = DATA;
ERROR:
if(in)
n_state = IDEL;
else
n_state = ERROR;
STOP:
if(!in)
n_state = DATA;
else
n_state = IDEL;
default:
n_state = IDEL;
endcase
end
always@(posedge clk)begin
if(reset)
done <= 1'b0;
else case(n_state)
STOP:done <= 1'b1;
default:done <= 1'b0;
endcase
end
always@(posedge clk)begin
if(reset)
cnt <= 4'd0;
else if(state == DATA)begin
if(cnt==4'd8)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= cnt;
end
endmodule
35. (3.2.5.18)serial receiver and datapath
在上题的基础上增加一个将接收到的8-bit有效信号输出。
设计一个reg变量out_byte_temp去接收数据,在DATA状态, out_byte_temp通过移位操作完成串并转换,也就是接收了8-bit数据;在STOP状态,将out_byte_temp赋值给out_byte输出。
增加的程序
reg [7:0]out_tmp;
always@(posedge clk)begin
if(reset)begin
done <= 1'b0;
end
else case(n_state)
STOP:begin
done <= 1'b1;
out_byte <= out_tmp;
end
default:begin
done <= 1'b0;
out_byte <= 'd0;
end
endcase
end
always@(posedge clk )begin
if(reset)
out_tmp <= 'd0;
else if(n_state == DATA) //注意这里是n_state
out_tmp <= {in,out_tmp[7:1]};
else
out_tmp <= out_tmp;
end
36. (3.2.5.19)serial receiver with parity checking 奇偶校验
parity: 在码流中每检测到一个1,则输出odd翻转一次,而数据位+奇校验位=9位数据中若满足要求则有奇数个1,那么odd输出则为1,若不满足校验要求,则是偶数个1使得校验位odd输出为0。也就是说这个奇偶校验模块实际上是一个判断位,用于对9位数据做奇校验判断。
因为只能使用该模块对9位数据做校验,所以在不接收9位数据的其他时态需要一定对该模块复位。
在上题的基础上,需要在STOP状态拉高done信号,在本题增加了奇校验后,需要在此模块同时对odd信号做判断,若为真,需说明校验成功,同时输出数据和拉高done信号;若为0,则说明数据接收时序没问题,但接收的数据不符合校验要求(传输过程出现错误),不输出数据、不拉高done信号。
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
); //
wire odd;
wire reset_odd;//复位信号无效或者在接收数据时,不对奇偶校验模块复位,其它时间一直对该模块复位
reg [3:0]cnt;
reg [7:0]out_tmp;
reg [7:0]state,n_state;
parameter IDEL =4'b0001,
DATA =4'b0010,
ERROR =4'b0100,
STOP =4'b1000;
//状态机第一段
always@(posedge clk)begin
if(reset)
state <= IDEL;
else
state <= n_state;
end
//状态机第二段
always@(*)begin
if(reset)
n_state = IDEL;
else case(state)
IDEL:
if(!in)
n_state = DATA;
else
n_state = IDEL;
DATA:
if(cnt=='d9)begin
if(in)
n_state = STOP;
else
n_state = ERROR;
end
else
n_state = DATA;
ERROR:
if(in)
n_state = IDEL;
else
n_state = ERROR;
STOP:
if(!in)
n_state = DATA;
else
n_state = IDEL;
default:n_state = IDEL;
endcase
end
//状态机第三段
always@(posedge clk)begin
if(reset)
done <= 1'b0;
else case(n_state)
STOP:begin
if(odd)
done <= 1'b1;
else
done <= 1'b0;
out_byte <= out_tmp;
end
default:begin
done <= 1'b0;
out_byte <= 'd0;
end
endcase
end
//输出数据计数器
always@(posedge clk)begin
if(reset)
cnt <= 'd0;
else if(state == DATA)begin
if(cnt == 'd9)
cnt <= 'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= cnt;
end
//数据寄存
always@(posedge clk)begin
if(reset)
out_tmp <= 'd0;
else if(state == DATA && cnt <= 'd7)
out_tmp <= {in,out_tmp[7:1]};
else
out_tmp <= out_tmp;
end
//奇偶校验复位,在不接收9位数据的其他时态需要一定对该模块复位。
assign reset_odd = reset | (!(state == DATA));
parity parity(
.clk (clk),
.reset (reset_odd),
.in (in),
.odd (odd)
);
endmodule
37. (3.2.5.20)sequence recognition 序列检测
检测到连续的5个1就拉高disc一个周期(一定要是111_110这种结构,不能是11111_1这种结构,这种属于检测到了连续的6个1,下同);检测到连续的6个1就拉高flag一个周期;检测到连续的7个1就拉高flag一个周期;检测到连续的8个1就拉高flag2个周期(从第7个开始,1越多flag高电平的时间越长)。
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err
);
//定义状态机状态
parameter S = 4'd0,
S1 = 4'd1,
S2 = 4'd2,
S3 = 4'd3,
S4 = 4'd4,
S5 = 4'd5,
DISC = 4'd6,
S6 = 4'd7,
FALG = 4'd8,
S7 = 4'd9,
ERRO = 4'd10;
reg [3:0] cur_state, next_state ;
//三段式状态机第一段
always @(posedge clk)begin
if(reset)
cur_state <= S;
else
cur_state <= next_state;
end
//三段式状态机第二段
always@(*)begin
next_state = S;
case(cur_state)
S : next_state = in ? S1 : S;
S1 : next_state = in ? S2 : S;
S2 : next_state = in ? S3 : S;
S3 : next_state = in ? S4 : S;
S4 : next_state = in ? S5 : S;
S5 : next_state = in ? S6 : DISC;
S6 : next_state = in ? ERRO : FALG;
DISC : next_state = in ? S1 : S;
FALG : next_state = in ? S1 : S;
ERRO : next_state = in ? ERRO : S;
default : next_state = S;
endcase
end
//三段式状态机第三段
always @(posedge clk)begin
if(reset)begin
disc <= 1'b0;
flag <= 1'b0;
err <= 1'b0;
end
else
case(next_state)
DISC:begin
disc <= 1'b1;
flag <= 1'b0;
err <= 1'b0;
end
FALG:begin
disc <= 1'b0;
flag <= 1'b1;
err <= 1'b0;
end
ERRO:begin
disc <= 1'b0;
flag <= 1'b0;
err <= 1'b1;
end
default:begin
disc <= 1'b0;
flag <= 1'b0;
err <= 1'b0;
end
endcase
end
endmodule
**38. (3.2.5.21)design a mealy FSM **
实现一个 Mealy 型有限状态机,该状态机可识别名为 x 的输入信号上的序列“101”。您的 FSM 应该有一个输出信号 z,当检测到“101”序列时,该信号被断言到 logic-1。您的 FSM 还应该具有低位有效异步复位。状态机中可能只有 3 种状态。您的 FSM 应该可以识别重叠的序列。
S :初始状态,检测到1后跳转到状态S1,不然保持在该状态,输出均为0
S1:检测到0后跳转到S2,检测到1维持在该状态
S2 :此时已经检测到了“10”,若继续检测到1,则“101”检测成功,状态跳转到S1(重复检测),且输出1;若检测到0,则跳转到S
module top_module (
input clk,
input aresetn, // Asynchronous active-low reset
input x,
output z );
parameter s0=3'b001,//初始状态
s1=3'b010,//
s2=3'b100;
reg[2:0]state, n_state;
always@(posedge clk or negedge aresetn)begin
if(!aresetn)
state <= s0;
else
state <= n_state;
end
always@(*)begin
if(!aresetn)
n_state = s0;
else case(state)
s0:
if(x)
n_state = s1;
else
n_state = s0;
s1:
if(!x)
n_state = s2;
else
n_state = s1;
s2:
if(x)
n_state = s1;//重叠检测
else
n_state = s0;
default:n_state = s0;
endcase
end
assign z = (state == s2 && x == 1'b1) ? 1'b1 : 1'b0; //mealy状态机少一个状态,输出时条件需加上最后一个状态的条件
//输出使用时序逻辑会延后一个周期
endmodule
39. (3.2.5.22)Serial two’s complementer (moore FSM)补码状态机
二进制补码:从LSB到MSB,第一个1时正式开始补码输出1,之后则开始取反(遇1输出0,遇0输出1。
module top_module (
input clk,
input areset,
input x,
output z
);
reg [2:0]state,n_state;
parameter A=3'b001,
B=3'b010,
C=3'b100;
always@(posedge clk or posedge areset)begin
if(areset)
state <= A;
else
state <= n_state;
end
always@(*)begin
if(areset)
n_state = A;
else case(state)
A: n_state = (x==1) ? B : A;
B: n_state = (x==1) ? C : B;
C: n_state = (x==1) ? C : B;
default: n_state = A;
endcase
end
assign z = (state == B) ;//取现态而不是次态
endmodule
40. (3.2.5.23)Serial two’s complementer (mealy FSM)补码状态机
A:检测到1,立马输出1,且跳转到下一个状态B;检测到0则继续保持该状态,说明没有检测到第一个1
B: 检测到0,则输出相反的1,状态不变;检测到1,则输出相反的0,状态不变
module top_module (
input clk,
input areset,
input x,
output z
);
reg [1:0]state,n_state;
parameter A=2'b001,
B=2'b010;
always@(posedge clk or posedge areset)begin
if(areset)
state <= A;
else
state <= n_state;
end
always@(*)begin
if(areset)
n_state = A;
else case(state)
A: n_state = (x==1) ? B : A;
B: n_state = B;
default: n_state = A;
endcase
end
assign z = (state==A && x==1) | (state == B && x==0) ;
endmodule
41. (3.2.5.24)Q3a FSM
大概意思是状态机复位状态在A,检测到s为1后进入状态B,在状态B每隔三个时钟周期就检查一次这三个时钟周期内w为高的时钟周期个数是否为2,若是则拉高输出z一个周期。
状态机设计两个状态A、B。在状态B设计一个计数器1从0-2循环计数,这样就把B状态分隔成了每3个周期的小段。再设计一个计数器2,来计数输入w为1的个数。这样就可以根据w为高的次数来对应的拉高输出z。
module top_module (
input clk,
input reset, // Synchronous reset
input s,
input w,
output z
);
reg [1:0]cnt1,cnt2;
reg [1:0]state,n_state;
parameter A=2'b01,
B=2'b10;
always@(posedge clk)begin
if(reset)
state <= A;
else
state <= n_state;
end
always@(*)begin
if(reset)
n_state = A;
else case(state)
A:
if(s==1)
n_state = B;
else
n_state = A;
B:
n_state = B;
endcase
end
always@(posedge clk)begin
if(reset)
z <= 1'b0;
else case(n_state)
A: z <= 1'b0;
B:
if(cnt1=='d2)begin
if((cnt2=='d1&&w)|(cnt2=='d2&&!w))
z <= 1'b1;
else
z <= 1'b0;
end
else
z <= 1'b0;
default: z<= 1'b0;
endcase
end
always@(posedge clk)begin
if (reset)
cnt1 <= 'd0;
else if(state == B)begin
if(cnt1=='d2)
cnt1 <= 'd0;
else
cnt1 <= cnt1 + 1'b1;
end
else
cnt1 <= 'd0;
end
always@(posedge clk)begin
if(reset)
cnt2 <= 'd0;
else if(state == B)begin
if(cnt1=='d2)
cnt2 <= 'd0;
else if(w)
cnt2 <= cnt2 + 1'b1;
else
cnt2 <= cnt2;
end
else
cnt2 <= 'd0;
end
endmodule
42. (3.2.5.26)Q3c FSM logic
这题基本上跟上题是一样的,只不过现态变成了输入,而不是中间变量。
module top_module (
input clk,
input [2:0] y,
input x,
output Y0,
output z
);
reg [2:0]state,n_state;
parameter A=3'b000,
B=3'b001,
C=3'b010,
D=3'b011,
E=3'b100;
always@(*)begin
case(y)
A: n_state = (x) ? B : A;
B: n_state = (x) ? E : B;
C: n_state = (x) ? B : C;
D: n_state = (x) ? C : B;
E: n_state = (x) ? E : D;
default:n_state = A;
endcase
end
assign Y0 = (n_state == B)|(n_state == D);
assign z = (y == D)|(y == E);
endmodule
43. (3.2.5.28)Q6c FSM one-hot next-state logic
为下一状态信号 Y2 和 Y4 编写逻辑表达式
always @(*) begin
next_state[0] <= w ? y[1] || y[4] : 1'b0;
next_state[1] <= w ? 1'b0 : y[1];
next_state[2] <= w ? 1'b0 : y[6] || y[2];
next_state[3] <= w ? y[2] || y[3] || y[5] || y[6]: 1'b0;
next_state[4] <= w ? 1'b0 : y[5] || y[3];
next_state[5] <= w ? 1'b0 : y[4];
end
assign Y2 = next_state[1];
assign Y4 = next_state[3];
或
always@(*)begin
case(y[6:1])
A:next=w?A:B;
B:next=w?D:C;
C:next=w?D:E;
D:next=w?A:F;
E:next=w?D:E;
F:next=w?D:C;
default:;
endcase
end
assign Y2 = y[1]&&(~w);
assign Y4 = w&&(y[2] || y[3] || y[5] || y[6]);
44. (3.2.5.33)another FSM
只要断言了复位输入,FSM就保持在开始状态,称为状态a。当复位信号被取消断言时,下一个时钟边缘之后,FSM必须将输出f设为1,持续一个时钟周期。然后,FSM必须监控x的输入。当x在三个连续的时钟周期中产生值1,0,1时,那么g应该在下一个时钟周期中设为1。当g = 1时,FSM必须监控y的输入。如果在最多两个时钟周期内y值为1,则FSM需要永久保持g = 1(即直到复位)。但是如果在两个时钟周期内y没有变为1,则FSM应该将g永久设置为0(直到复位)。
IDLE : 初始状态,复位结束后跳转到状态OUT_F
OUT_F: 这个状态用来将f拉高一个时钟周期
S : 序列“101”检测的初始状态,若检测到x为1,则跳转到状态S1;若检测到,则保持该状态
S1 : 若检测到x为0,则跳转到状态S10;若检测到x为1,则保持该状态
S10 : 若检测到x为1,则跳转到状态S101;若检测到x为0,则跳转到状态S
S101 : 此时已经检测到了序列“101”,需要判断接下来y是否变成了1:若检测到y为1,则跳转到状态P1,说明输 出g应该永久拉高;若检测到y为0,说明第一个周期不满足条件,则跳转到状态JUG,在状态JUG在判断是否满足条件
P1 : 永远保持在此状态,用来输出永恒的g=1
JUG : 若检测到y为1,则跳转到状态P1,说明输出g应该永久拉高;若检测到y为0,则跳转到状态P0,说明输出g应该永久拉拉低
P0 : 永远保持在此状态,用来输出永恒的g=0
module top_module (
input clk,
input resetn, // active-low synchronous reset
input x,
input y,
output f,
output g
);
reg [3:0]state,n_state;
parameter IDEL=4'b0000, //初始状态
OUT_F =4'b0001, //输出f的状态
C =4'b0010, //检测“101”序列的初始状态
C1 =4'b0011, //检测‘1’
C10 =4'b0100, //检测‘10’
C101=4'b0101, //检测‘101’
G1 =4'b0110, //输出永久的g=1
E =4'b0111, //检测
G0 =4'b1000; //输出永久的g=0
always@(posedge clk)begin
if(!resetn)
state <= IDEL;
else
state <= n_state;
end
always@(*)begin
if(!resetn)
n_state = IDEL;
else case(state)
IDEL: n_state = OUT_F;
OUT_F:n_state = C;
C: n_state = (x) ? C1 :C;
C1: n_state = (!x)? C10 :C1; //如果是0则变为10正确,如果是1则11继续检测0
C10: n_state = (x) ? C101:C; //如果是1则跳到101正确,如果是0则100检测错误回到序列检测初始状态
C101: n_state = (y) ? G1 : E; //y=1则跳到G1永久输出g=1,y=0则跳到E继续检测
G1: n_state = G1;
E: n_state = (y) ? G1 : G0; //y=1则跳到G1永久输出g=1,y=0则跳到G0输出g=0
G0: n_state = G0;
default: n_state = IDEL;
endcase
end
always@(posedge clk)begin
if(!resetn)begin
f <= 1'b0;
g <= 1'b0;
end
else case(n_state)
OUT_F:f<=1'b1;
C101:g<=1'b1;//
G1:g<=1'b1;
E:g<=1'b1; //C101和E状态也输出g=1
default:begin
f <= 1'b0;
g <= 1'b0;
end
endcase
end
endmodule
45. (3.3.4)enable shift register
为控制移位寄存器的FSM的一部分,我们希望能够在检测到合适的位模式时,使移位寄存器精确地为4个时钟周期。我们在Exams/review2015_fsmseq中处理序列检测,所以FSM的这部分只处理4个周期的移位寄存器启用。
当FSM被重置时,置位shift_ena 4个周期,然后0永远(直到重置)。
这道题的意思是,如果复位信号有效,shift_ena信号就为1;当复位信号撤销以后,shift_ena信号保持4个周期高电平后变为0。
使用一个计数器计数,reset该计数器复位到0,shift_ena高电平期间计数(在此期间计数0-3),计数到3后计数器清零。
shift_ena在复位有效后拉高,在计数器计数到3后拉低。
module top_module (
input clk,
input reset, // Synchronous reset
output shift_ena);
reg [3:0]cnt;
always@(posedge clk)begin
if(reset)
shift_ena <= 1'b1;
else if(cnt == 'd3)
shift_ena <= 1'b0;
else
shift_ena <= shift_ena;
end
always@(posedge clk)begin
if(reset)
cnt <= 'd0;
else if(shift_ena) //shift_ena有效时cnt自加
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
endmodule
46. (3.3.5)FSM:The complete FSM
首先检测序列“1101”,检测到后停止检测,拉高shift_ena4个时钟周期,然后拉高counting直到输入done_counting为1,然后拉高done直到输入ack为1。
S:初始状态,开始检测1101序列的第一个1,若检测到1,则跳转到S1,若检测到0,则保持该状态
S1:若检测到1,则说明可能是1101序列的第二个1,所以状态跳转到S11, 若检测到0,则状态跳转到S,去检测1101的第一个1
S11:若检测到0,则说明可能是1101序列的0,所以状态跳转到S110, 若检测到1,则保持在该状态
S110:若检测到1,则说明是1101序列的最后一个1,所以状态跳转到B0, 若检测到0,则状态跳转到S,去检测1101的第一个1
B0:直接跳转到B1
B1:直接跳转到B2
B2:直接跳转到B3
B3:直接跳转到COUNT
COUNT:当counting为1时跳转到状态WAIT,不然一直停留在该状态
WAIT:当ack为0时跳转到状态S,不然一直停留在该状态
module top_module (
input clk,
input reset, // Synchronous reset
input data,
output shift_ena,
output counting,
input done_counting,
output done,
input ack );
reg [3:0]cur_state,next_state;
parameter S = 4'd0,
S1 = 4'd1,
S11 = 4'd2,
S110 = 4'd3,
B0 = 4'd4,
B1 = 4'd5,
B2 = 4'd6,
B3 = 4'd7,
COUNT = 4'd8,
WAIT = 4'd9;
always@(posedge clk)begin
if(reset)
cur_state <= S;
else
cur_state <= next_state;
end
always@(*)begin
//if(reset)
//next_state = S;
//else
case(cur_state)
S:
next_state = (data) ? S1 :S;
S1:
next_state = (data) ? S11 :S;
S11:
next_state = (!data) ? S110 :S11;
S110:
next_state = (data) ? B0 :S;//如果data为1则检测到序列1101,立刻跳到B0状态,不需要状态1101,加这个状态会延后一个周期
B0:
next_state = B1;
B1:
next_state = B2;
B2:
next_state = B3;
B3:
next_state = COUNT;
COUNT:
next_state = (done_counting) ? WAIT : COUNT;
WAIT:
next_state = (ack) ? S : WAIT;
default: next_state = S;
endcase
end
assign shift_ena = (cur_state == B0)||(cur_state == B1)||(cur_state == B2)||(cur_state == B3);
assign counting = (cur_state == COUNT);
assign done = (cur_state == WAIT);
endmodule
47. (3.3.6)the complete timer
在数据流中检测到序列 1101 后,电路需要将接下来的 4bit 数据移入移位寄存器。4bit 数据决定了计数器的计数周期,称为 delay[3:0]。首先到达的比特作为数据的高位。在 FSM 中增加计数器状态,计数周期为 (delay[3:0] + 1 )* 1000 个时钟周期。同时输出 count 当前剩余的计数周期,输出当前剩余计数周期的千位(比如,还剩1000个周期输出 1,还剩 999 个周期时输出 0)。当计数停止后,count 的输出可以为任意数。
当计数完成后,电路置高 done 信号通知上层应用计数器计数完成,等待 ack 信号置高后,状态机清除 done 信号,返回空闲状态等待捕获下一个 1101 序列。
思路:
首先需要找到序列“1101”,接下来的码流输出的4个数据(高位在前)+1,再乘以1000,就是接下来要延时的时间。延时完成后,拉高done,直到ack拉高(响应了),再重新开始新一轮。count输出的是计数的千位数,比如计数器还是1999,那么输出为1。
S : 初始状态,检测到1,则跳转到S1,否则保持该状态
S1 : 检测到1,则跳转到S11,否则跳转到S
S11 : 检测到0,则跳转到S110,否则保持该状态
S110 : 检测到1,则跳转到DELAY ,否则跳转到S
DELAY : 在次状态延时4个时钟周期(cnt_delay从0-3),用来接收输入码流上接下来的4个数据,作为延时的时间
COUNT : 延时状态,直到延时到指定的时间
WAIT : 在次状态等待akc响应信号
module top_module (
input clk,
input reset, // Synchronous reset
input data,
output [3:0] count,
output counting,
output done,
input ack );
reg [15:0]cnt;
reg [1:0]cnt_delay;
reg [3:0]delay;
reg[2:0]state,n_state;
parameter S = 3'd0,
S1 = 3'd1,
S11 = 3'd2,
S110 = 3'd3,
DELAY = 3'd4,
COUNT = 3'd5,
WAIT = 3'd6;
always@(posedge clk)begin
if(reset)
state <= S;
else
state <= n_state;
end
always@(*)begin
if(reset)
n_state = S;
else case(state)
S:
n_state = (data) ? S1 : S;
S1:
n_state = (data) ? S11 : S;
S11:
n_state = (!data) ? S110 : S11;
S110:
n_state = (data) ? DELAY : S;
DELAY:begin
if(cnt_delay=='d3)
n_state = COUNT;
else
n_state = DELAY;
end
COUNT:begin
if(cnt=='d0)
n_state = WAIT;
else
n_state = COUNT;
end
WAIT:
n_state = (!ack) ? WAIT : S;
default: n_state = S;
endcase
end
assign count = cnt/1000;
assign counting=(state == COUNT);
assign done = (state == WAIT);
always@(posedge clk)begin
if(reset)
cnt <= 'd0;
else if(state == DELAY)
cnt <= (delay + 1'b1) *1000 -1'b1;
else if(state == COUNT)
cnt <= cnt - 1'b1;
else
cnt <= cnt;
end
always@(posedge clk)begin
if(reset)
cnt_delay <= 'd0;
else if(state == DELAY)
cnt_delay <= cnt_delay + 1'b1;
else
cnt_delay <= cnt_delay;
end
always@(*)begin
if(state == DELAY)
case(cnt_delay)
0: delay[3] = data;
1: delay[2] = data;
2: delay[1] = data;
3: delay[0] = data;
default:;
endcase
else
delay = 4'b0000;
end
endmodule
48. (3.3.7)FSM:one-hot logic equations
module top_module(
input d,
input done_counting,
input ack,
input [9:0] state, // 10-bit one-hot current state
output B3_next,
output S_next,
output S1_next,
output Count_next,
output Wait_next,
output done,
output counting,
output shift_ena
); //
// You may use these parameters to access state bits using e.g., state[B2] instead of state[6].
parameter S=0, S1=1, S11=2, S110=3, B0=4, B1=5, B2=6, B3=7, Count=8, Wait=9;
assign B3_next = (state[B2]);
assign S_next = (!d && state[S]) || (!d && state[S1]) || (!d && state[S110]) || (ack && state[Wait]);
assign S1_next = (d && state[S]);
assign Count_next = (state[B3])||(!done_counting && state[Count]);
assign Wait_next = (done_counting && state[Count]) || (!ack && state[Wait]);
assign done = (state[Wait]);
assign counting = (state[Count]);
assign shift_ena = (state[B0])||(state[B1])||(state[B2])||(state[B3]);
endmodule
49. (4.2.5)combinational circuit 5
根据下面的时序图实现这个组合逻辑电路。
可以看出这是一个4输入、1输出的组合电路,且输出是根据c的取值来的,所以这个是个4选1电路(MUX4),所以可以用case语句来根据c的取值来进行输出。
module top_module (
input [3:0] a,
input [3:0] b,
input [3:0] c,
input [3:0] d,
input [3:0] e,
output [3:0] q );
always@(*)begin
case(c)
4'd0: q = b;
4'd1: q = e;
4'd2: q = a;
4'd3: q = d;
default: q = 4'hf;
endcase
end
endmodule
50. (4.2.10)sequential circuit 10
可以看到当输出q为高电平时,a、b、state三个中总是有奇数个高电平,所以q是a、b、state三个的偶校验位:q = a ^ b ^ state;
再来观察state的变化,state的变化都发生在(a == b)时,且变化的值为a(或者说b),当a不等于b时,state保持不变。
module top_module (
input clk,
input a,
input b,
output q,
output state );
assign q = a ^ b ^ state;
always @(posedge clk)begin
if(a == b)
state <= a;
else
state <= state;
end
endmodule