参考链接:HDLBits导学
Problem 131 One-hot FSM / Fsm onehot
问题:下图是具有1个输入和2个输出的状态转移图:
假设该状态机使用独热码,state[0]到state[9]分别对应于状态S0到S9。图中没有标注的输出均为0。
下面请实现状态机的状态转移逻辑和输出逻辑(不需要实现状态触发器)。 模块的输入是当前状态state[9:0],输出下个状态next_state[9:0]和电路的两个输出out1和out2。请通过假设输入独热码入去推导状态转移的逻辑方程。(提供的测试平台将使用非独热码的输入进行测试,以确保你没有尝试做更复杂的事情
思路:使用在Problem 123中的热编码方式
解决:
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;
assign next_state[S0] = (state[S0]&~in) || (state[S1]&~in) || (state[S2]&~in) || (state[S3]&~in) || (state[S4]&~in) ||
(state[S7]&~in) || (state[S8]&~in) || (state[S9]&~in);
assign next_state[S1] = (state[S0]&in) ||(state[S8]&in) || (state[S9]&in);
assign next_state[S2] = (state[S1]&in);
assign next_state[S3] = (state[S2]&in);
assign next_state[S4] = (state[S3]&in);
assign next_state[S5] = (state[S4]&in);
assign next_state[S6] = (state[S5]&in);
assign next_state[S7] = (state[S6]&in)||(state[S7]&in);
assign next_state[S8] = (state[S5]&~in);
assign next_state[S9] = (state[S6]&~in);
assign out1 = (state[S8] || state[S9]);
assign out2 = (state[S9] || state[S7]);
endmodule
Problem 132 PS/2 packet parser / Fsm ps2
问题:PS/2 鼠标协议发送三个字节长的消息。然而,在一个连续的字节流中,消息的开始和结束位置并不明显。唯一的指示是每个三字节消息的第一个字节总是有bit[3]=1(但其他两个字节的 bit[3] 可能是 1 或 0,具体取决于数据)。
我们需要一个有限状态机,当给定输入字节流时,它将搜索消息边界。我们将使用的算法是丢弃字节,直到我们看到bit[3]=1 的字节。然后我们假设这是消息的第 1 个字节,并在接收到所有 3 个字节后(完成)发出消息接收信号。
FSM 应该在每个消息的第三个字节被成功接收后立即在周期内发出完成信号
一些时序图来解释所需的行为
在无错误条件下,每三个字节形成一个消息:
发生错误时,搜索字节 1:
请注意,这与1xx序列识别器不同。此处不允许重叠序列:
解决:
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output done); //
//三个状态 空闲 检测到之后开始 完成
parameter Idle=0,Start=1,Done=2;
reg [1:0] state,next_state;
//记录字节数 也就是时钟数
reg [1:0] cnt;
// State transition logic (combinational)
always @(*) begin
case(state)
Idle: next_state = in[3] ? Start : Idle;
Start: next_state = (cnt==2'd2) ? Done : Start;
//在完成状态时 也需要检测 针对连续字节有效的情况 但输出信号有效时间只是一个时钟周期,所以不管怎样done状态只存在一个时钟周期
Done: next_state = in[3] ? Start : Idle;
endcase
end
// State flip-flops (sequential)
always @(posedge clk) begin
if(reset)
state <= Idle;
else begin
state <= next_state;
if(next_state==Start)
cnt <= cnt + 1;
else
cnt <= 2'd0;
end
end
// Output logic
assign done = (state==Done);
endmodule
注意:注意有效字节连续出现的情况,在完成状态下也要进行检测,也就是注意状态的转换了
写完才发现原来还有份官方给的状态转换图,按照状态转换图来写会简单得多
Problem 133 PS/2 packet parser and datapath / Fsm ps2data
问题:现在,已经写了一个PS/2接口的状态机,该状态机可以标识PS/2字节流中的三字节消息。请在这个状态机中添加一条数据路径,该数据路径可以在接收数据包的同时输出24bits(3字节)的消息(out_bytes[23:16]为第一字节,out_bytes[15:8]为第二字节,以此类推)。
当发出接收完成信号done时,out_bytes必须是有效的,其他时候可以输出任何的内容(即不在乎输出什么。)
例子:
解决:
module top_module(
input clk,
input [7:0] in,
input reset, // Synchronous reset
output [23:0] out_bytes,
output done); //
parameter Idle=0,Start=1,Done=2;
reg [1:0] state,next_state;
reg [1:0] cnt;
// State transition logic (combinational)
always @(*) begin
case(state)
Idle: next_state = in[3] ? Start : Idle;
Start: next_state = (cnt==2'd2) ? Done : Start;
Done: next_state = in[3] ? Start : Idle;
endcase
end
// State flip-flops (sequential)
always @(posedge clk) begin
if(reset)
state <= Idle;
else begin
state <= next_state;
if(next_state==Start) begin
cnt <= cnt + 1;
//保存第一个字节
if(cnt == 2'd0)
out_bytes[23:16] <= in[7:0];
end
else
cnt <= 2'd0;
if(state == Start) begin
//保存后两个字节
case(cnt)
2'd1:out_bytes[15:8] <= in[7:0];
2'd2:out_bytes[7:0] <= in[7:0];
endcase
end
end
end
// Output logic
assign done = (state==Done);
endmodule
(用之前的写法写这个题还是挺复杂的)
Problem 134 Serial Receiver
问题:在许多(较旧的)串行通信协议中,每个数据字节都与一个起始位和一个停止位一起发送,以帮助接收器从位流中分隔字节。一种常见的方案是使用 1 个起始位 (0)、8 个数据位和 1 个停止位 (1)。当没有传输任何内容(空闲)时,该线路也处于逻辑 1。
设计一个有限状态机,当给定比特流时,它将识别何时正确接收字节。它需要识别起始位,等待所有 8 个数据位,然后验证停止位是否正确。如果停止位没有按预期出现,FSM 必须等待直到找到停止位,但是此次接收是错误的,不产生输出,然后才能尝试接收下一个字节
解决:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output done
);
//状态说明
//空闲状态 开始状态 接收中状态 停止位检查状态 错误状态 接收完成状态
parameter Idle=0,Start=1,Receiving=2,Stop=3,Error=4,Done=5;
reg [2:0] state,next_state;
reg [3:0] cnt;
always @(*) begin
case(state)
Idle: next_state = in ? Idle : Start;
Start: next_state = Receiving;
Receiving: next_state = (cnt==4'd7) ? Stop : Receiving;
Stop: next_state = in ? Done : Error;
Error: next_state = in ? Idle : Error;
Done: next_state = in ? Idle : Start;
endcase
end
always @(posedge clk) begin
if(reset)
state <= Idle;
else begin
state <= next_state;
if(next_state==Receiving)
cnt <= cnt + 1'b1;
else
cnt <= 4'd0;
end
end
assign done = (state==Done);
endmodule
比较疑惑为什么组合逻辑这么写就有问题了?
always @(*) begin
case(state)
Idle: next_state = in ? Idle : Receiving;
//Start: next_state = Receiving;
Receiving: next_state = (cnt==4'd8) ? Stop : Receiving;
Stop: next_state = in ? Done : Error;
Error: next_state = in ? Idle : Error;
Done: next_state = in ? Idle : Receiving;
endcase
end
Problem 135 Serial Receiver and Datapath
问题:既然您有一个有限状态机,它可以识别在串行比特流中正确接收字节的时间,请添加一个数据路径,以输出正确接收的数据字节。out_byte需要在done为1时有效,否则无关紧要。
请注意,串行协议首先发送最低有效位
一些时序图
解决:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
);
parameter Idle=0,Start=1,Receiving=2,Stop=3,Error=4,Done=5;
reg [2:0] state,next_state;
reg [3:0] cnt;
always @(*) begin
case(state)
Idle: next_state = in ? Idle : Start;
Start: next_state = Receiving;
Receiving: next_state = (cnt==4'd7) ? Stop : Receiving;
Stop: next_state = in ? Done : Error;
Error: next_state = in ? Idle : Error;
Done: next_state = in ? Idle : Start;
endcase
end
always @(posedge clk) begin
if(reset)
state <= Idle;
else
state <= next_state;
end
//和状态转换逻辑分开来写好看点
always @(posedge clk) begin
if(reset)
cnt <= 4'd0;
else begin
if(next_state==Receiving) begin
cnt <= cnt + 1'b1;
out_byte[cnt] <= in;
end
else
cnt <= 4'd0;
if(next_state==Stop)//下一个状态为stop状态时 是数据的最后一位
out_byte[7] <= in;
end
end
assign done = (state==Done);
endmodule
Problem 136 Serial Receiver with Parity Checking
问题:这题仍然是在前面的基础上进行进化,增添了奇偶校验位。奇偶校验(Parity Check)是一种校验代码传输正确性的方法。根据被传输的一组二进制代码的数位中“1”的个数是奇数或偶数来进行校验。采用奇数的称为奇校验,反之,称为偶校验。奇偶校验是在传输中保障数据接收正确的常用方法,也是最初级的校验方式。
该题采用的是奇校验的方式,并且提供了奇偶校验模块。原本 start 和 stop 位之间的8 bit 变为了9 bit,新增的1 bit 为奇校验位,从而使得这9 bit 中“1”的数量为奇数个,即题目中提供的奇偶校验模块输出为1时表面数据正确,否则数据错误不予接收。波形图如下所示
module parity (
input clk,
input reset,
input in,
output reg odd);
always @(posedge clk)
if (reset) odd <= 0;
else if (in) odd <= ~odd;
endmodule
一些时序图
没有帧错误。第一个字节奇偶校验通过,第二个字节失败
思路:对奇偶校验模块的理解,每输入一个1输出的状态都会取反一下,所以,奇数个1的话最后的输出为1
解决:
module top_module(
input clk,
input in,
input reset, // Synchronous reset
output [7:0] out_byte,
output done
);
//新增check状态 检查是否正确
parameter Idle=0,Start=1,Receiving=2,Stop=3,Error=4,Done=5,Check=6;
reg [2:0] state,next_state;
reg [3:0] cnt;
wire odd;
//例化模块
parity(
.clk(clk),
//复位 下一个状态为开始 则复位
.reset(reset|| next_state == Start),
.in(in),
.odd(odd)
);
always @(*) begin
case(state)
Idle: next_state = in ? Idle : Start;
Start: next_state = Receiving;
Receiving: next_state = (cnt==4'd7) ? Check : Receiving;
//若前面一个字节有奇数个1 那么第9位应该为0,且此时odd输出为1,所以两个不相等 进入停止位检查状态 负责进入错误状态
Check: next_state = (in != odd) ? Stop : Error;
Stop: next_state = in ? Done : Error;
Error: next_state = in ? Idle : Error;
Done: next_state = in ? Idle : Start;
endcase
end
always @(posedge clk) begin
if(reset)
state <= Idle;
else begin
state <= next_state;
end
end
always @(posedge clk) begin
if(reset)
cnt <= 4'd0;
else begin
if(next_state==Receiving) begin
cnt <= cnt + 1'b1;
out_byte[cnt] <= in;
end
else
cnt <= 4'd0;
if(next_state==Check)
out_byte[7] <= in;
end
end
assign done = (state==Done);
endmodule
(咳,这题怎么这么顺利,,,)
Problem 137 Sequuence recognition / Fsm hdlc
问题:同步HDLC帧涉及从连续的比特流中解码寻找某一帧(即数据包)的开始和结束位置的位模式。如果接收到连续的6个1(即01111110),即是帧边界的“标志”。同时为了避免输入的数据流中意外包含这个帧边界“标志”,数据的发送方必须在数据中连续的5个1之后插入一个0,而数据的接收方必须将这个多余的0检测出来并丢弃掉。同时,如果输入检测到了了连续7个或更多的1时,接收方还需要发出错误信号
下面请创建一个有限状态机来识别上述的三个序列:
- 输入0111110:插入的信号位需要丢弃(Discarded, disc)。
- 输入01111110:标记(Flag)帧的开始/结束(flag)。
- 输入01111111 ...:错误(Error)(7个或更多的1)(err)。
重置FSM时,其状态应恢复到之前输入0的状态
以下是一些示例序列,用于说明所需的操作
丢弃0111110:
标志01111110 :
重置行为和错误01111111...:
解决:
module top_module(
input clk,
input reset, // Synchronous reset
input in,
output disc,
output flag,
output err);
//状态说明
//没有检测到1 接收状态 抛弃0状态 标志位状态 错误状态
parameter No_one=0,Receving=1,Disc=2,Flag=3,Error=4;
reg [2:0] state,next_state;
reg [3:0] cnt;
always @(*) begin
case(state)
No_one: next_state = in ? Receving : No_one;
//接收状态下 如果输入为1 先判断1的个数是否超出 当cnt为6时,下一个时钟沿到来了 输入还是1 那么1的个数就超了 所以这个位置是6
//如果输入不为1 判断此时的计数器的值为5 进入Disc状态 ,为6则进入Flag状态 其他的到没有检测到1的状态
Receving: next_state = in ? (cnt>=3'd6 ? Error : Receving) : (cnt==5 ? Disc : (cnt==6 ? Flag : No_one));
Disc:next_state = in ? Receving : No_one;
Flag:next_state = in ? Receving : No_one;
Error:next_state = in ? Error : No_one;
endcase
end
always @(posedge clk) begin
if(reset)
state <= No_one;
else
state <= next_state;
end
always @(posedge clk) begin
if(reset)
cnt <= 3'd0;
else
if(next_state == Receving)
cnt <= cnt + 1'b1;
else
cnt <= 3'd0;
end
assign disc = (state == Disc);
assign flag = (state == Flag);
assign err = (state == Error);
endmodule