HDLbits day10
5.10Lemmings1
在旅鼠的 2D 世界中,旅鼠可以处于以下两种状态之一:向左行走或向右行走。如果遇到障碍物,它会切换方向。特别是,如果 Lemming 撞到左边,它会向右走。如果它撞到右边,它会向左走。如果它同时在两侧碰撞,它仍然会切换方向。
实现一个具有两个状态、两个输入和一个输出的摩尔状态机来模拟这种行为。
module top_module( input clk, input areset, // 刚洗脑的袋鼠向左走. input bump_left, input bump_right, output walk_left, output walk_right); reg state, next_state; parameter LEFT=0,RIGHT=1; always @(*) begin // State transition logic case(state) LEFT:next_state<=(bump_left==1)?RIGHT:LEFT; RIGHT:next_state<=(bump_right==1)?LEFT:RIGHT; endcase end always @(posedge clk, posedge areset) begin // State flip-flops with asynchronous reset if(areset) state<=LEFT; else state<=next_state; end // Output logic assign walk_left = (state == LEFT); assign walk_right = (state == RIGHT); endmodule
5.11、Lemmings2
除了左右走动和碰撞时改变方向外,当ground=0时,旅鼠会摔倒并说“啊啊!”。当地面重新出现 ( ground=1 ) 时,旅鼠将继续沿与坠落前相同的方向行走。跌倒时被撞不影响行走方向,与地面消失(但尚未跌倒)同一个周期被撞,或仍在跌倒时再次出现地面时,也不影响行走方向。
构建一个模拟这种行为的有限状态机。
1)在1的基础上多了坠落状态,且在掉落时方向与碰撞无关;
2)因为要记录掉落前的方向所以分为fl,fr(向左掉落,向右掉落)两个状态。
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,FALL_L=2,FALL_R=3; reg [1:0] state,next_state; always@(*) begin case(state) LEFT:begin if(ground==0) next_state<=FALL_L; else if(bump_left) next_state<=RIGHT; else next_state<=LEFT; end RIGHT:begin if(ground==0) next_state<=FALL_R; else if(bump_right) next_state<=LEFT; else next_state<=RIGHT; end FALL_L:next_state<=(ground==0)?FALL_L:LEFT; FALL_R:next_state<=(ground==0)?FALL_R:RIGHT; endcase end always@(posedge clk or posedge areset) begin if(areset) state<=LEFT; else state<=next_state; end assign walk_left=(state==LEFT); assign walk_right=(state==RIGHT); //assign aaah=(ground==0);错误 assign aaah=(state==FALL_L|state==FALL_R); endmodule
5.12、Lemmings3
除了走路和摔倒之外,旅鼠有时会被告知做一些有用的事情,比如挖掘(当dig=1时它开始挖掘)。如果旅鼠当前在地面上行走(ground=1并且没有下落),它可以挖掘,并且会继续挖掘直到它到达另一边(ground=0)。到那时,由于没有地面,它会掉下来(啊啊!),然后一旦再次撞到地面,就继续沿原来的方向行走。与坠落一样,挖掘时被撞到没有效果,并且在坠落或没有地面时被告知要挖掘被忽略。
(换句话说,行走的旅鼠可以跌倒、挖掘或切换方向。如果满足这些条件中的一个以上,则跌倒的优先级高于 dig,而 dig 的优先级高于切换方向。)
扩展您的有限状态机来模拟这种行为。
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=0,RIGHT=1,FALL_L=2,FALL_R=3,DIG_L=4,DIG_R=5; reg [2:0] state,next_state; always@(*) begin case(state) LEFT:next_state<=ground?(dig?DIG_L:(bump_left?RIGHT:LEFT)):FALL_L; RIGHT:next_state<=ground?(dig?DIG_R:(bump_right?LEFT:RIGHT)):FALL_R; FALL_L:next_state<=ground?LEFT:FALL_L; FALL_R:next_state<=ground?RIGHT:FALL_R; DIG_L:next_state<=ground?DIG_L:FALL_L; DIG_R:next_state<=ground?DIG_R:FALL_R; endcase end always@(posedge clk or posedge areset) begin if(areset) state<=LEFT; else state<=next_state; end assign walk_left=(state==LEFT); assign walk_right=(state==RIGHT); assign aaah=(state==FALL_L|state==FALL_R); assign digging=(state==DIG_L|state==DIG_R); endmodule
5.13、Lemmings4
虽然旅鼠可以行走、跌倒和挖掘,但旅鼠并非无懈可击。如果旅鼠跌落太久然后撞到地面,它可能会飞溅。特别是,如果 Lemming 跌落超过 20 个时钟周期然后撞到地面,它会飞溅并停止行走、跌落或挖掘(所有 4 个输出变为 0),永远(或直到 FSM 重置)。旅鼠在落地前可以坠落的距离没有上限。旅鼠只有在落地时才会飞溅;它们不会在半空中飞溅。
扩展您的有限状态机来模拟这种行为。
跌倒20个周期是可以生存的,跌倒 21 个周期会导致飞溅。
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=0,RIGHT=1,FALL_L=2,FALL_R=3,DIG_L=4,DIG_R=5,SPLAT=6; reg [2:0] state,next_state; integer count; //下一个状态 always@(*) begin case(state) LEFT:next_state<=ground?(dig?DIG_L:(bump_left?RIGHT:LEFT)):FALL_L; RIGHT:next_state<=ground?(dig?DIG_R:(bump_right?LEFT:RIGHT)):FALL_R; FALL_L:next_state<=ground?(count>19?SPLAT:LEFT):FALL_L; FALL_R:next_state<=ground?(count>19?SPLAT:RIGHT):FALL_R; DIG_L:next_state<=ground?DIG_L:FALL_L; DIG_R:next_state<=ground?DIG_R:FALL_R; SPLAT:next_state<=SPLAT; endcase end //状态交换 always@(posedge clk or posedge areset) begin if(areset) state<=LEFT; else state<=next_state; end //计数器 always@(posedge clk or posedge areset) begin if(areset) count<=0; else if(state==FALL_L|state==FALL_R) count<=count+1; else count<=0; end assign walk_left=(state==LEFT); assign walk_right=(state==RIGHT); assign aaah=(state==FALL_L|state==FALL_R); assign digging=(state==DIG_L|state==DIG_R); /*always @(*) 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; SPLAT: {walk_left,walk_right,aaah,digging} = 4'b0000; endcase */ endmodule
5.14、Fsm onehot
给定以下具有 1 个输入和 2 个输出的状态机:
假设此状态机使用 one-hot 编码,其中state[0]到state[9]分别对应于状态 S0 到 S9。除非另有说明,否则输出为零。
实现状态机的状态转换逻辑和输出逻辑部分(但不是状态触发器)。您在state[9:0]中获得当前状态,并且必须生成next_state[9:0]和两个输出。假设 one-hot 编码,通过检查推导逻辑方程。(测试台将使用非一个热输入进行测试,以确保您不会尝试做更复杂的事情)。
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] = ~in & (state[s0]|state[s1]|state[s2]|state[s3]|state[s4]|state[s7]|state[s8]|state[9]); 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]==1|state[s9]==1); assign out2=(state[s7]==1|state[s9]==1); endmodule
5.15、Fsm ps2
PS/2 鼠标协议发送三个字节长的消息。然而,在连续的字节流中,消息的开始和结束位置并不明显。唯一的指示是每个三字节消息的第一个字节总是有bit[3]=1(但其他两个字节的bit[3]可能是1或0,具体取决于数据)。
我们想要一个有限状态机,当给定输入字节流时,它将搜索消息边界。我们将使用的算法是丢弃字节,直到我们看到一个带有bit[3]=1的字节。然后我们假设这是消息的第 1 个字节,并在收到所有 3 个字节后发出消息的接收信号(完成)。
FSM 应该在成功接收到每个消息的第三个字节后立即在循环中 发出完成信号。
检测一个3字节序列,该3字节格式中,第一个字节的bit[3]是1,其他不知道。
因此,当判断到bit[3]=1时,表示一次传输的开始,连续接收3字节后再判断。
module top_module( input clk, input [7:0] in, input reset, // 同步复位 output done); // parameter BYTE1=0,BYTE2=1,BYTE3=2,DONE=3; reg[1:0] state,next_state; // State transition logic (combinational) always@(*) begin case(state) BYTE1:next_state<=in[3]?BYTE2:BYTE1; BYTE2:next_state<=BYTE3; BYTE3:next_state<=DONE; DONE:next_state<=in[3]?BYTE2:BYTE1; endcase end // State flip-flops (sequential) always@(posedge clk) begin if(reset) state<=BYTE1; else state<=next_state; end // Output logic assign done=(state==DONE); endmodule
5.16、Fsm ps2data
现在您有了一个状态机,可以识别 PS/2 字节流中的三字节消息,添加一个数据路径,该路径也将在收到数据包时输出 24 位(3 字节)消息(out_bytes[23:16]是第一个字节,out_bytes[15:8]是第二个字节,依此类推)。
每当断言完成信号时, out_bytes 都需要有效。您可以在其他时间输出任何内容(即不关心)。
大概意思:在上一题的基础上,将接收的3个字节数据,在接收完后完整的输出。
在上一题的基础上,通过寄存器先寄存收到的数据,最后合并输出。
module top_module( input clk, input [7:0] in, input reset, // Synchronous reset output [23:0] out_bytes, output done); // parameter BYTE1=0,BYTE2=1,BYTE3=2,DONE=3; reg[1:0] state,next_state; reg[23:0] data; // FSM from fsm_ps2 always@(*) begin case(state) BYTE1:next_state<=in[3]?BYTE2:BYTE1; BYTE2:next_state<=BYTE3; BYTE3:next_state<=DONE; DONE:next_state<=in[3]?BYTE2:BYTE1; endcase end always@(posedge clk) begin if(reset) state<=BYTE1; else state<=next_state; end // New: Datapath to store incoming bytes. assign done = (state == DONE); always @(posedge clk) begin if (reset) begin data <= 24'd0; end else begin data[23:16] <= data[15:8]; data[15:8] <= data[7:0]; data[7:0] <= in; end end assign out_bytes = (done) ? data : 24'd0; endmodule