关注 望森FPGA 查看更多FPGA资讯
这是望森的第 17 期分享
作者 | 望森
来源 | 望森FPGA
目录
1 Lemmings 1
2 Lemmings 2
3 Lemmings 3
4 Lemmings 4
5 One-hot FSM | 独热 FSM
6 PS/2 packet parser | PS/2 数据包解析器
7 PS/2 packet parser anddatapath | PS/2 数据包解析器和数据路径
本文中的代码都能够正常运行,请放心食用😋~
练习的官方网站是:https://hdlbits.01xz.net/
注:作者将每个练习的知识点都放在了题目和答案之后
1 Lemmings 1
题目:
游戏《旅鼠》涉及的动物大脑相当简单。非常简单,我们将使用有限状态机对其进行建模。
在《旅鼠》的 2D 世界中,旅鼠可以处于两种状态之一:向左行走或向右行走。如果撞到障碍物,它会改变方向。具体来说,如果旅鼠左侧受到撞击,它会向右行走。如果右侧受到撞击,它会向左行走。如果两侧同时受到撞击,它仍会改变方向。
实现一个 Moore 状态机,该状态机具有两个状态、两个输入和一个输出,可以对此行为进行建模。
答案:
我的答案:
module top_module(input clk,input areset, // Freshly brainwashed Lemmings walk left.input bump_left,input bump_right,output walk_left,output walk_right); // // parameter LEFT=0, RIGHT=1, ...reg state, next_state;parameter LEFT=1'b0, RIGHT=1'b1; wire LEFT2LEFT,LEFT2RIGHT,RIGHT2RIGHT,RIGHT2LEFT;always @(posedge clk or posedge areset) begin // This is a sequential always block// State flip-flops with asynchronous resetif(areset)beginstate <= LEFT;endelse beginstate <= next_state;endendalways @(*) begin // This is a combinational always block// State transition logiccase(state)LEFT : beginif (LEFT2LEFT)beginnext_state = LEFT;endelse if (LEFT2RIGHT)beginnext_state = RIGHT;endendRIGHT : beginif (RIGHT2RIGHT)beginnext_state = RIGHT;endelse if (RIGHT2LEFT)beginnext_state = LEFT;endenddefault next_state = state;endcaseendassign LEFT2LEFT = !bump_left;assign LEFT2RIGHT = bump_left;assign RIGHT2RIGHT = !bump_right;assign RIGHT2LEFT = bump_right;// Output logic// assign out = (state == ...);assign walk_left = (state == LEFT);assign walk_right = (state == RIGHT);endmodule参考答案:
module top_module (input clk,input areset,input bump_left,input bump_right,output walk_left,output walk_right
);// Give state names and assignments. I'm lazy, so I like to use decimal numbers.// It doesn't really matter what assignment is used, as long as they're unique.parameter WL=0, WR=1;reg state;reg next;// Combinational always block for state transition logic. Given the current state and inputs,// what should be next state be?// Combinational always block: Use blocking assignments. always@(*) begincase (state)WL: next = bump_left ? WR : WL;WR: next = bump_right ? WL : WR;endcaseend// Combinational always block for state transition logic. Given the current state and inputs,// what should be next state be?// Combinational always block: Use blocking assignments. always @(posedge clk, posedge areset) beginif (areset) state <= WL;else state <= next;end// Combinational output logic. In this problem, an assign statement are the simplest.// In more complex circuits, a combinational always block may be more suitable. assign walk_left = (state==WL);assign walk_right = (state==WR);endmodule
知识点:
提示:
2 Lemmings 2
题目:
除了左右行走外,如果脚下的地面消失,旅鼠还会摔倒(大概会发出“啊啊啊”的声音)。
除了左右行走和被撞时改变方向外,当地面 = 0 时,旅鼠还会摔倒并发出“啊啊啊”的声音。当地面重新出现(地面 = 1 时),旅鼠会继续沿摔倒前的方向行走。摔倒时被撞不会影响行走方向,在地面消失(但尚未摔倒)的同一周期内被撞,或者在摔倒时地面重新出现,也不会影响行走方向。
构建一个有限状态机来模拟此行为。
答案:
1.状态转换图
2.代码
module top_module(input clk,input areset, // Freshly brainwashed Lemmings walk left.input bump_left,input bump_right,input ground,output walk_left,output walk_right,output aaah ); // parameter LEFT=0, RIGHT=1, ...reg [1:0] state, next_state;parameter LEFT=2'd0, RIGHT=2'd1,FALL_LEFT=2'd2,FALL_RIGHT=2'd3; wire LEFT2LEFT,LEFT2RIGHT,LEFT2FALL_LEFT,RIGHT2RIGHT,RIGHT2LEFT,RIGHT2FALL_RIGHT,FALL_LEFT2FALL_LEFT,FALL_LEFT2LEFT,FALL_RIGHT2FALL_RIGHT,FALL_RIGHT2RIGHT;always @(posedge clk or posedge areset) begin // This is a sequential always block// State flip-flops with asynchronous resetif(areset)beginstate <= LEFT;endelse beginstate <= next_state;endendalways @(*) begin // This is a combinational always block// State transition logiccase(state)LEFT : beginif (LEFT2LEFT)beginnext_state = LEFT;endelse if (LEFT2RIGHT)beginnext_state = RIGHT;endelse if (LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endendRIGHT : beginif (RIGHT2RIGHT)beginnext_state = RIGHT;endelse if (RIGHT2LEFT)beginnext_state = LEFT;endelse if (RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endendFALL_LEFT : beginif (FALL_LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endelse if (FALL_LEFT2LEFT)beginnext_state = LEFT;endendFALL_RIGHT : beginif (FALL_RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endelse if (FALL_RIGHT2RIGHT)beginnext_state = RIGHT;endenddefault next_state = state;endcaseendassign LEFT2LEFT = !bump_left && ground;assign LEFT2RIGHT = bump_left && ground;assign LEFT2FALL_LEFT = !ground;assign RIGHT2RIGHT = !bump_right && ground;assign RIGHT2LEFT = bump_right && ground;assign RIGHT2FALL_RIGHT = !ground;assign FALL_LEFT2FALL_LEFT = !ground;assign FALL_LEFT2LEFT = ground;assign FALL_RIGHT2FALL_RIGHT = !ground;assign FALL_RIGHT2RIGHT = ground;// Output logic// assign out = (state == ...);assign walk_left = (state == LEFT);assign walk_right = (state == RIGHT);assign aaah = (state == FALL_LEFT) || (state == FALL_RIGHT);endmodule
知识点:
提示:
3 Lemmings 3
题目:
除了行走和跌倒,有时还可以命令旅鼠做一些有用的事情,比如挖掘(当 dig=1 时,它会开始挖掘)。如果旅鼠当前正在地面上行走( ground=1 且没有跌倒),它可以挖掘,并会继续挖掘直到到达另一侧( ground=0 )。此时,由于没有地面,它会跌倒(aaah!),然后再次接触地面后继续沿其原始方向行走。与跌倒一样,在挖掘时受到撞击不会产生任何影响,并且在跌倒或没有地面时被告知挖掘会被忽略。
(换句话说,行走的旅鼠可以跌倒、挖掘或切换方向。如果满足这些条件中的多个条件,则跌倒的优先级高于挖掘,而挖掘的优先级高于切换方向。)
扩展您的有限状态机以模拟此行为。
答案:
1.状态转换图
2.代码
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 ); reg [2:0] state, next_state;parameter LEFT=3'd0, RIGHT=3'd1,DIG_LEFT=3'd2,DIG_RIGHT=3'd3,FALL_LEFT=3'd4,FALL_RIGHT=3'd5; wire LEFT2LEFT,LEFT2RIGHT,LEFT2DIG_LEFT,LEFT2FALL_LEFT,RIGHT2RIGHT,RIGHT2LEFT,RIGHT2DIG_RIGHT,RIGHT2FALL_RIGHT,DIG_LEFT2FALL_LEFT,DIG_LEFT2DIG_LEFT,DIG_RIGHT2FALL_RIGHT,DIG_RIGHT2DIG_RIGHT,FALL_LEFT2FALL_LEFT,FALL_LEFT2LEFT,FALL_RIGHT2FALL_RIGHT,FALL_RIGHT2RIGHT;always @(posedge clk or posedge areset) begin // This is a sequential always block// State flip-flops with asynchronous resetif(areset)beginstate <= LEFT;endelse beginstate <= next_state;endendalways @(*) begin // This is a combinational always block// State transition logiccase(state)LEFT : beginif (LEFT2LEFT)beginnext_state = LEFT;endelse if (LEFT2RIGHT)beginnext_state = RIGHT;endelse if (LEFT2DIG_LEFT)beginnext_state = DIG_LEFT;endelse if (LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endendRIGHT : beginif (RIGHT2RIGHT)beginnext_state = RIGHT;endelse if (RIGHT2LEFT)beginnext_state = LEFT;endelse if (RIGHT2DIG_RIGHT)beginnext_state = DIG_RIGHT;endelse if (RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endendDIG_LEFT : beginif (DIG_LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endelse if (DIG_LEFT2DIG_LEFT)beginnext_state = DIG_LEFT;endendDIG_RIGHT : beginif (DIG_RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endelse if (DIG_RIGHT2DIG_RIGHT)beginnext_state = DIG_RIGHT;endendFALL_LEFT : beginif (FALL_LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endelse if (FALL_LEFT2LEFT)beginnext_state = LEFT;endendFALL_RIGHT : beginif (FALL_RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endelse if (FALL_RIGHT2RIGHT)beginnext_state = RIGHT;endenddefault next_state = LEFT;endcaseend/*bump_left,bump_right,ground,dig,*/assign LEFT2LEFT = !bump_left && ground && !dig;assign LEFT2RIGHT = bump_left && ground && !dig;assign LEFT2DIG_LEFT = ground && dig;assign LEFT2FALL_LEFT = !ground;assign RIGHT2RIGHT = !bump_right && ground && !dig;assign RIGHT2LEFT = bump_right && ground && !dig;assign RIGHT2DIG_RIGHT = ground && dig;assign RIGHT2FALL_RIGHT = !ground;assign DIG_LEFT2FALL_LEFT = !ground;assign DIG_LEFT2DIG_LEFT = ground;assign DIG_RIGHT2FALL_RIGHT = !ground;assign DIG_RIGHT2DIG_RIGHT = ground ;assign FALL_LEFT2FALL_LEFT = !ground;assign FALL_LEFT2LEFT = ground;assign FALL_RIGHT2FALL_RIGHT = !ground;assign FALL_RIGHT2RIGHT = ground;// Output logicassign walk_left = (state == LEFT);assign walk_right = (state == RIGHT);assign aaah = (state == FALL_LEFT) || (state == FALL_RIGHT);assign digging = (state == DIG_LEFT) || (state == DIG_RIGHT);endmodule
知识点:
提示:
4 Lemmings 4
题目:
尽管旅鼠可以行走、跌倒和挖洞,但它们并非无懈可击。如果旅鼠跌倒时间过长,然后撞到地面,它就会 splatter 四分五裂。具体来说,如果旅鼠跌倒超过 20 个时钟周期,然后撞到地面,它就会四分五裂并停止行走、跌倒或挖洞(所有 4 个输出都变为 0),永远(或直到 FSM 重置)。旅鼠在撞到地面之前可以跌倒多远没有上限。旅鼠只有在撞到地面时才会四分五裂;它们不会在半空中四分五裂。
扩展您的有限状态机以模拟此行为。
跌倒 20 个周期是可以存活的:
跌落21次导致四分五裂:
答案:
1.状态转换图
2.代码
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 ); reg [2:0] state, next_state;parameter LEFT=3'd0, RIGHT=3'd1,DIG_LEFT=3'd2,DIG_RIGHT=3'd3,FALL_LEFT=3'd4,FALL_RIGHT=3'd5,SLATTER=3'd6; reg [4:0] cnt_fall;reg FALL_20clk;wire LEFT2LEFT,LEFT2RIGHT,LEFT2DIG_LEFT,LEFT2FALL_LEFT,RIGHT2RIGHT,RIGHT2LEFT,RIGHT2DIG_RIGHT,RIGHT2FALL_RIGHT,DIG_LEFT2FALL_LEFT,DIG_LEFT2DIG_LEFT,DIG_RIGHT2FALL_RIGHT,DIG_RIGHT2DIG_RIGHT,FALL_LEFT2FALL_LEFT,FALL_LEFT2LEFT,FALL_LEFT2SLATTER,FALL_RIGHT2FALL_RIGHT,FALL_RIGHT2RIGHT,FALL_RIGHT2SLATTER;//FALL counter & 20clk flagalways @(posedge clk or posedge areset) beginif(areset)begincnt_fall <= 5'd0;endelse if(state == FALL_LEFT || state == FALL_RIGHT)begincnt_fall <= cnt_fall + 5'd1;endelse begincnt_fall <= 5'd0;endendalways @(*) begin if(areset)beginFALL_20clk = 1'd0;endelse if(cnt_fall > 5'd19)beginFALL_20clk = 1'd1;endelse beginFALL_20clk = FALL_20clk;endendalways @(posedge clk or posedge areset) beginif(areset)beginstate <= LEFT;endelse beginstate <= next_state;endendalways @(*) begincase(state)LEFT : beginif (LEFT2LEFT)beginnext_state = LEFT;endelse if (LEFT2RIGHT)beginnext_state = RIGHT;endelse if (LEFT2DIG_LEFT)beginnext_state = DIG_LEFT;endelse if (LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endendRIGHT : beginif (RIGHT2RIGHT)beginnext_state = RIGHT;endelse if (RIGHT2LEFT)beginnext_state = LEFT;endelse if (RIGHT2DIG_RIGHT)beginnext_state = DIG_RIGHT;endelse if (RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endendDIG_LEFT : beginif (DIG_LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endelse if (DIG_LEFT2DIG_LEFT)beginnext_state = DIG_LEFT;endendDIG_RIGHT : beginif (DIG_RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endelse if (DIG_RIGHT2DIG_RIGHT)beginnext_state = DIG_RIGHT;endendFALL_LEFT : beginif (FALL_LEFT2FALL_LEFT)beginnext_state = FALL_LEFT;endelse if (FALL_LEFT2LEFT)beginnext_state = LEFT;endelse if (FALL_LEFT2SLATTER)beginnext_state = SLATTER;endendFALL_RIGHT : beginif (FALL_RIGHT2FALL_RIGHT)beginnext_state = FALL_RIGHT;endelse if (FALL_RIGHT2RIGHT)beginnext_state = RIGHT;endelse if (FALL_RIGHT2SLATTER)beginnext_state = SLATTER;endendSLATTER : beginnext_state = SLATTER;enddefault next_state = LEFT;endcaseend/*bump_left,bump_right,ground,dig,*/assign LEFT2LEFT = !bump_left && ground && !dig;assign LEFT2RIGHT = bump_left && ground && !dig;assign LEFT2DIG_LEFT = ground && dig;assign LEFT2FALL_LEFT = !ground;assign RIGHT2RIGHT = !bump_right && ground && !dig;assign RIGHT2LEFT = bump_right && ground && !dig;assign RIGHT2DIG_RIGHT = ground && dig;assign RIGHT2FALL_RIGHT = !ground;assign DIG_LEFT2FALL_LEFT = !ground;assign DIG_LEFT2DIG_LEFT = ground;assign DIG_RIGHT2FALL_RIGHT = !ground;assign DIG_RIGHT2DIG_RIGHT = ground ;assign FALL_LEFT2FALL_LEFT = !ground;assign FALL_LEFT2LEFT = ground && !FALL_20clk;assign FALL_LEFT2SLATTER = ground && FALL_20clk;assign FALL_RIGHT2FALL_RIGHT = !ground;assign FALL_RIGHT2RIGHT = ground && !FALL_20clk;assign FALL_RIGHT2SLATTER = ground && FALL_20clk;// Output logicassign walk_left = (state == LEFT);assign walk_right = (state == RIGHT);assign aaah = (state == FALL_LEFT) || (state == FALL_RIGHT);assign digging = (state == DIG_LEFT) || (state == DIG_RIGHT);endmodule
知识点:
提示:使用 FSM 来控制计数器,追踪 Lemming 下落的时间长度。
5 One-hot FSM | 独热 FSM
题目:
给定以下具有 1 个输入和 2 个输出的状态机:
假设此状态机使用独热编码,其中 state[0] 到 state[9] 分别对应于状态 S0 到 S9。除非另有说明,否则输出为零。
实现状态机的状态转换逻辑和输出逻辑部分(但不是状态触发器)。您将在 state[9:0] 中得到当前状态,并且必须生成 next_state[9:0] 和两个输出。通过检查得出逻辑方程,假设采用独热编码。(测试台将使用非独热输入进行测试,以确保您不会尝试执行更复杂的事情)。
提示:
通过查看状态转换图的入边,可以推导出独热状态转换逻辑的逻辑方程。
答案:
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[S7] | state[S9] ; endmodule
6 PS/2 packet parser | PS/2 数据包解析器
题目:
PS/2 鼠标协议发送三个字节长的消息。但是,在连续的字节流中,消息的开始和结束位置并不明显。唯一的迹象是每个三字节消息的第一个字节始终具有 bit[3]=1(但其他两个字节的 bit[3] 可能为 1 或 0,具体取决于数据)。
我们想要一个有限状态机,当给定输入字节流时,它将搜索消息边界。我们将使用的算法是丢弃字节,直到我们看到 bit[3]=1 的字节。然后我们假设这是消息的第 1 个字节,并在收到所有 3 个字节(完成)后发出消息接收信号。
在成功接收每条消息的第三个字节后,FSM 应立即在循环中发出完成信号。
一些时序图用于解释所需行为
在无错误的情况下,每三个字节形成一条消息:
当发生错误时,搜索字节1:
请注意,这与 1xx 序列识别器不同。这里不允许重叠序列:
答案:
1.状态转换图
2.代码
module top_module(input clk,input [7:0] in,input reset, // Synchronous resetoutput done); //reg [1:0] state, next_state;parameter BYTE1=2'd0, BYTE2=2'd1, BYTE3=2'd2, DONE=2'd3; wire BYTE12BYTE1,BYTE12BYTE2,DONE2BYTE1,DONE2BYTE2;// State transition logic (combinational)always @(posedge clk) beginif(reset)beginstate <= BYTE1;endelse beginstate <= next_state;endendalways @(*) begincase(state)BYTE1 : beginif (BYTE12BYTE1)beginnext_state = BYTE1;endelse if (BYTE12BYTE2)beginnext_state = BYTE2;endendBYTE2 : beginnext_state = BYTE3;endBYTE3 : beginnext_state = DONE;endDONE : beginif (DONE2BYTE1)beginnext_state = BYTE1;endelse if (DONE2BYTE2)beginnext_state = BYTE2;endenddefault next_state = 2'd0;endcaseendassign BYTE12BYTE1 = in[3] == 0;assign BYTE12BYTE2 = in[3] == 1;assign DONE2BYTE1 = in[3] == 0;assign DONE2BYTE2 = in[3] == 1;// Output logicassign done = (state == DONE);endmodule
知识点:
提示:
-
尽管 in[7:0] 是一个字节,但 FSM 只有一个输入:in[3]。
-
您需要 4 个状态。三个状态可能行不通,因为其中一个状态需要断言 done,而对于每个收到的消息,done 只会断言一个周期。
7 PS/2 packet parser anddatapath | PS/2 数据包解析器和数据路径
题目:
现在您有了一个可以识别 PS/2 字节流中的三字节消息的状态机,请添加一个数据路径,该数据路径还会在收到数据包时输出 24 位(3 字节)消息(out_bytes[23:16] 是第一个字节,out_bytes[15:8] 是第二个字节,等等)。
只要 done 信号被置位,out_bytes 就必须有效。您可以在其他时间输出任何内容(即,无关紧要)。
例如:
答案:
module top_module(input clk,input [7:0] in,input reset, // Synchronous resetoutput [23:0] out_bytes,output done); //// FSM from fsm_ps2reg [1:0] state, next_state;parameter BYTE1=2'd0, BYTE2=2'd1, BYTE3=2'd2, DONE=2'd3; wire BYTE12BYTE1,BYTE12BYTE2,DONE2BYTE1,DONE2BYTE2;// State transition logic (combinational)always @(posedge clk) beginif(reset)beginstate <= BYTE1;endelse beginstate <= next_state;endendalways @(*) begincase(state)BYTE1 : beginif (BYTE12BYTE1)beginnext_state = BYTE1;endelse if (BYTE12BYTE2)beginnext_state = BYTE2;endendBYTE2 : beginnext_state = BYTE3;endBYTE3 : beginnext_state = DONE;endDONE : beginif (DONE2BYTE1)beginnext_state = BYTE1;endelse if (DONE2BYTE2)beginnext_state = BYTE2;endenddefault next_state = 2'd0;endcaseendassign BYTE12BYTE1 = in[3] == 0;assign BYTE12BYTE2 = in[3] == 1;assign DONE2BYTE1 = in[3] == 0;assign DONE2BYTE2 = in[3] == 1;// Output logicassign done = (state == DONE);
// New: Datapath to store incoming bytes.reg [23:0] in_temp;always @(posedge clk) beginin_temp <= {in_temp[15:0],in};endassign out_bytes = (state == DONE) ? in_temp : 0 ;endmodule
知识点:
提示:
使用 PS/2 数据包解析器的 FSM 并添加数据路径来捕获传入的字节。
讲解:
在上题基础上,增加三个字节的寄存器,在 DONE 状态时输出数据即可。
- END -
公z号/CSDN搜索【望森FPGA】,查看更多FPGA资讯~
相关推荐文章,点击跳转:
望森FPGA的HDLBits合集