目录
1.Exams/review2015 count1k
建立一个从 0 到 999 的计数器,包括 0 到 999,周期为 1000 个周期。复位输入是同步的,应将计数器复位为 0。
module top_module (
input clk,
input reset,
output [9:0] q);
always@(posedge clk) begin
if(reset) q <= 0; //复位后q赋值为零
else if(q<999) q <= q+1; //如果q<999 那么+1
else q <= 0; //如果q==999,清零
end
endmodule
编写verilog代码时重要的点就是时序,如上代码所示,q会在reset给进之后的下一拍才会置为0
2.Exams/review2015 shiftcount
构建一个四位移位寄存器,该寄存器也用作递减计数器。当shift_ena为 1时,数据首先移入最高有效位。当count_ena为 1时,当前在移位寄存器中的数字递减。由于整个系统不会同时使用shift_ena和count_ena,因此您的电路无关紧要如果两个控制输入都为 1(这主要意味着哪种情况获得更高优先级并不重要)
module top_module (
input clk,
input shift_ena,
input count_ena,
input data,
output [3:0] q);
always @(posedge clk) begin
if(shift_ena) begin //shift_ena 和 count_ena 的优先级无关
q <= {q[2:0],data}; //移位操作将数据写入
end
else if(count_ena) begin
q <= q -1;
end
else begin
q<=q;
end
end
endmodule
如果说要考虑优先级,只需要将两个if条件的前后顺序就行调换就行,但是那样就不能同时输入shift_ena 和count_ena因为同一时刻只能处理一个,如果有需求同时输入,就要修改设计代码;
3.Exams/review2015 fsmseq
构建一个有限状态机,在输入比特流中搜索序列 1101。找到序列后,应将start_shifting设置为 1,直到重置。陷入最终状态旨在模拟在尚未实现的更大 FSM 中进入其他状态。我们将在接下来的几个练习中扩展这个 FSM。
module top_module( //mothod 1 use the state_machine
input clk,
input reset, // Synchronous reset
input data,
output start_shifting);
parameter S0=0,S1=1,S2=2,S3=3,PICK=4;
reg [2:0] cur_st;
reg [2:0] nxt_st;
always@(posedge clk) begin //state change 1
if(reset) cur_st <= S0;
else cur_st <= nxt_st;
end
always @(*) begin //caculte the nxt_st
case(cur_st)
S0:nxt_st = data ? S1:S0;
S1:nxt_st = data ? S2:S0;
S2:nxt_st = data ? S2:S3;
S3:nxt_st = data ? PICK:S0;
PICK:nxt_st = data ? S2:S0;
default : nxt_st = S0;
endcase
end
//start_shifting
always@(posedge clk) begin //the output start_shifting
if(reset) start_shifting <= 0;
else if(nxt_st == PICK) start_shifting <= 1; //use nxt_st,because use sequential logic
else start_shifting <= start_shifting;
end
endmodule
module top_module ( //mothod 2 use the shifting_register
input clk,
input reset, // Synchronous reset
input data,
output start_shifting);
reg [3:0] data_seq; //4_bits register to match 1101
always@(posedge clk)begin
if(reset) data_seq <= 0;
else data_seq <= {data_seq[2:0],data}; //shifting the input data
end
always@(*)begin //use the comb logic so the reset need to flap
if(reset_r) start_shifting=0; //you can try use the sequential logic instead
else if(data_seq == 'b1101) start_shifting = 1;
else start_shifting = start_shifting;
end
reg reset_r;
always@(posedge clk)begin
reset_r <= reset;
end
endmodule
- 使用状态机编写代码时,注意该时刻的状态是由之前所有输入的决定的,再该时刻使用组合逻辑根据输入进行下一状态的计算,然后在下一个上升沿到来时修改cur_st 的值;
- 个人认为状态机你可以使用nxt_st 或者cur_st 来进行最后输出的计算,关键在于这两个st之间差了一拍而已,如果有需要可用nxt_st,但是目前我见过的状态机都是使用的cur_st;
- 使用移位寄存器时注意的点就是由于使用组合逻辑对输出进行计算,所以reset信号要打一拍使用,不然start_shifting信号就与reset信号同拍了;
4.Exams/review2015 fsmshift
作为用于控制移位寄存器的 FSM 的一部分,我们希望能够在检测到正确的位模式时启用移位寄存器恰好 4 个时钟周期。我们在Exams/review2015_fsmseq中处理序列检测,因此 FSM 的这一部分仅处理启用 4 个周期的移位寄存器。
每当 FSM 复位时,将shift_ena置位4 个周期,然后永远为 0(直到复位)。
module top_module ( //state machine
input clk,
input reset, // Synchronous reset
output shift_ena);
parameter S0=0,S1=1,S2=2,S3=3,S4=4;
reg [2:0] cur_st;
reg [2:0] nxt_st;
always @(posedge clk)begin
if(reset) cur_st <= S0;
else cur_st <= nxt_st;
end
always @(*) begin
case(cur_st)
S0:nxt_st = reset?S0:S1;
S1:nxt_st = reset?S0:S2;
S2:nxt_st = reset?S0:S3;
S3:nxt_st = reset?S0:S4;
S4:nxt_st = reset?S0:S4;
endcase
end
assign shift_ena = (cur_st == S0 | cur_st == S1 | cur_st == S2 | cur_st == S3);
endmodule
module top_module ( //method 2 reg cnt
input clk,
input reset,
output shift_ena
);
reg [2:0] cnt;
always @(posedge clk) begin
if(reset) cnt <= 0;
else if(cnt==4) cnt <= cnt;
else cnt <= cnt + 1;
end
assign shift_ena = (cnt <= 3);
endmodule
- 使用状态机时,中间使用4个状态进行控制4拍的动作,如果reset期间进入,之间跳到S0,组合逻辑根据cur_st输出shift_ena;
- method 2 的方法可以通过仿真,但是我好奇的一个点是当第一次reset没来时,cnt的值为多少,此时输出的shift_ena真的是对的?
5.Exams/review2015 fsm
我们想创建一个计时器:
- 当检测到特定模式 (1101) 时开始,
- 再移 4 位以确定延迟的持续时间,
- 等待计数器完成计数,并且
- 通知用户并等待用户确认计时器。
在这个问题中,只实现控制定时器的有限状态机。此处不包括数据路径(计数器和一些比较器)。
串行数据在数据输入引脚上可用。当接收到模式 1101 时,状态机必须断言输出shift_ena正好 4 个时钟周期。
之后,状态机断言其计数输出以指示它正在等待计数器,并一直等到输入done_counting为高。
此时,状态机必须断言完成以通知用户定时器已超时,并等待直到输入ack为 1,然后才被重置以寻找下一次出现的启动序列 (1101)。
状态机应重置为开始搜索输入序列 1101 的状态。
这是预期输入和输出的示例。'x' 状态读起来可能有点混乱。它们表明 FSM 不应该关心该周期中的特定输入信号。例如,一旦检测到 1101 模式,FSM 将不再查看数据输入,直到完成其他所有操作后恢复搜索。
module top_module (
input clk,
input reset, // Synchronous reset
input data,
output shift_ena,
output counting,
input done_counting,
output done,
input ack );
parameter S0=0,S1=1,S2=2,S3=3,B0=4,B1=5,B2=6,B3=7,COUNT=8,WAIT=9;
//S state use to match
reg [3:0] cur_st;
reg [3:0] nxt_st;
always @(posedge clk) begin
if(reset) cur_st <= S0;
else cur_st <= nxt_st;
end
always @(*) begin
case(cur_st)
S0: nxt_st = data?S1:S0;
S1: nxt_st = data?S2:S0;
S2: nxt_st = data?S2:S3;
S3: nxt_st = data?B0:S0;
B0: nxt_st = B1;
B1:nxt_st = B2;
B2:nxt_st = B3;
B3:nxt_st = COUNT;
COUNT:nxt_st = done_counting?WAIT:COUNT; //output the count
WAIT:nxt_st = ack?S0:WAIT; //output the done_counting
default : nxt_st = S0;
endcase
end
assign shift_ena = (cur_st == B0 | cur_st == B1 | cur_st == B2 | cur_st == B3);
assign counting = cur_st == COUNT;
assign done = cur_st == WAIT;
endmodule
- 中间使用B状态进行shift_ena 拉高4拍的控制;
- 后续的状态跳转就是输入的done_counting 和 ack 来进行;输出根据状态输出;使用的是cur_st 所以组合逻辑即可;
6.Exams/review2015 fsmonehot
给定以下具有 3 个输入、3 个输出和 10 个状态的状态机:
假设使用以下 one-hot 编码, 通过检查导出下一状态逻辑方程和输出逻辑方程: (S, S1, S11, S110, B0, B1, B2, B3, Count, Wait) = (10'b0000000001, 10 'b0000000010, 10'b0000000100, ... , 10'b1000000000)
假设 one-hot 编码,通过检查导出状态转换和输出逻辑方程。仅实现此状态机的状态转换逻辑和输出逻辑(组合逻辑部分)。(测试台将使用非一个热输入进行测试,以确保您不会尝试做更复杂的事情)。
编写生成以下等式的代码:
- B3_next -- 状态 B1 的下一个状态逻辑
- S_next
- S1_next
- Count_next
- 等待下一个
- 完成——输出逻辑
- 数数
- shift_ena
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 = ...;
// assign S_next = ...;
// etc.
assign B3_next = state[B2];
assign S_next = (state[S]&d==0) | (state[S1]&d==0) | (state[S110] && d==0) | (state[Wait] && ack==1)|(reset==1);
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
- 根据状态转移图判断哪种情况会进入该状态即可;
- 根据状态转移图中红色字体,编写相应输出即可;
7.Exams/review2015 fancytimer
我们想创建一个带有一个输入的计时器:
- 当检测到特定输入模式 (1101) 时启动,
- 再移 4 位以确定延迟的持续时间,
- 等待计数器完成计数,并且
- 通知用户并等待用户确认计时器。
串行数据在数据输入引脚上可用。当接收到模式 1101 时,电路必须移入接下来的 4 位,首先是最高有效位。这 4 位决定了定时器延迟的持续时间。我将其称为delay[3:0]。
之后,状态机断言其计数输出以指示它正在计数。状态机必须精确计数(delay[3:0] + 1) * 1000 个时钟周期。例如,delay=0 表示计数 1000 个周期,delay=5 表示计数 6000 个周期。同时输出当前剩余时间。这应该等于delay 1000 个周期,然后delay-1 1000 个周期,依此类推,直到 0 1000 个周期。当电路不计数时,count[3:0] 输出是无关紧要的(任何值方便您实现)。
此时,电路必须断言done以通知用户计时器已超时,并等待输入ack为 1,然后再复位以查找下一次出现的启动序列 (1101)。
电路应重置为开始搜索输入序列 1101 的状态。
这是预期输入和输出的示例。'x' 状态读起来可能有点混乱。它们表明 FSM 不应该关心该周期中的特定输入信号。例如,一旦读取了 1101 和 delay[3:0],电路就不再查看数据输入,直到在其他所有操作完成后恢复搜索。在本例中,电路计数 2000 个时钟周期,因为 delay[3:0] 值为 4'b0001。最后几个周期以 delay[3:0] = 4'b1110 开始另一个计数,它将计数 15000 个周期。
module top_module (
input clk,
input reset, // Synchronous reset
input data,
output [3:0] count,
output counting,
output done,
input ack );
parameter S0=0,S1=1,S2=2,S3=3,S4=4,S5=5,S6=6,S7=7,COUNT=8,WAIT=9;
reg [3:0] cur_st;
reg [3:0] nxt_st;
reg [3:0] delay; //the delay
reg [13:0] dly_cnt; //(delay + 1) * 1000
wire cnt_done;
always @(posedge clk)begin
if(reset) cur_st <= S0;
else cur_st <= nxt_st;
end
always@(*) begin
case(cur_st)
S0:nxt_st=data?S1:S0;
S1:nxt_st=data?S2:S0;
S2:nxt_st=data?S2:S3;
S3:nxt_st=data?S4:S0;
S4:nxt_st=S5;
S5:nxt_st=S6;
S6:nxt_st=S7;
S7: nxt_st=COUNT;
COUNT: nxt_st = cnt_done?WAIT:COUNT;
WAIT:nxt_st=ack?S0:WAIT;
default : nxt_st=S0;
endcase
end
wire con_dly;
assign con_dly = (cur_st == S5 | cur_st == S6| cur_st == S7 | cur_st == S4);
always@(posedge clk)begin
if(con_dly==1)begin
delay <= {delay[2:0],data}; //shift the data into delay
end
end
always@(posedge clk)begin
if(reset) dly_cnt <= 0;
else if(cur_st == COUNT) dly_cnt <= dly_cnt+1;
else dly_cnt <= 0;
end
assign cnt_done = (dly_cnt == (delay + 1) * 1000 - 1); //the dly_cnt do not cnt to the max
assign count = delay - dly_cnt / 1000;
assign counting = cur_st == COUNT;
assign done = cur_st == WAIT;
endmodule
- 状态转移以上面题目一样,这里就是在后续的S4,5,6,7状态时进行要delay的收集,注意所使用到的状态以及data;比如输入的数据为:11010011;cur_st跳变为S4的同时data会进入第一个0;
- 注意由于dly_cnt 使用的是时序逻辑,所以最后的dly_cnt并不会计数到最大值,只能计数到-1值;并且由于使用的是cur_st==COUNT 的条件,当cur_st跳变的COUNT时,dly_cnt 并不会马上进行跳变,而是要等下一拍;
注:以上题目来自:Exams/review2015 fancytimer - HDLBits 答案有些参考网络上其他答案,如有打扰请联系删除;