[吾题有解] HDLBits : Lemmings4

张开发
2026/4/18 17:20:15 15 分钟阅读

分享文章

[吾题有解] HDLBits : Lemmings4
本题是HDLBits旅鼠系列的最后一题旅鼠4相比于上一题的变化是增加了“当旅鼠下落超过20个时钟周期时其死亡输出全部变为为0且只能通过复位恢复”。到这里我们不难想到如下两点通过设置计数器对保持下落状态的周期进行计数通过设置新的状态——死亡SPLAT让旅鼠在满足一定条件的情况下从下落状态FALL_L、FALL_R进入,且非复位不得跳转至其他状态复位后回到LEFT状态。基于上述分析可以得到如下代码moduletop_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);localparam LEFT1,RIGHT2,FALL_L3,FALL_R4,DIG_L5,DIG_R6,SPLAT7;reg[2:0]state;reg[2:0]next_state;reg[6:0]cnt;// watch out this!always (posedge clk or posedge areset)if(areset)stateLEFT;elsestatenext_state;always (posedge clk or posedge areset)if(areset)cnt7d0;elseif(stateFALL_L||stateFALL_R)cntcnt1b1;elsecnt7d0;always (*)case(state)LEFT:next_state~ground?FALL_L:(dig?DIG_L:(bump_left?RIGHT:LEFT));RIGHT:next_state~ground?FALL_R:(dig?DIG_R:(bump_right?LEFT:RIGHT));// Why there is cnt 5d19?FALL_L:next_stateground?(cnt5d19?SPLAT:LEFT):FALL_L;FALL_R:next_stateground?(cnt5d19?SPLAT:RIGHT):FALL_R;DIG_L:next_state~ground?FALL_L:DIG_L;DIG_R:next_state~ground?FALL_R:DIG_R;SPLAT:next_stateSPLAT;endcase assign walk_left(stateLEFT);assign walk_right(stateRIGHT);assign aaah(stateFALL_L)||(stateFALL_R);assign digging(stateDIG_L)||(stateDIG_R);endmodule这里解释一下代码中计数器寄存器以及判断逻辑中给出注释的部分。复位后计数器从0开始计数第一个下落状态在时钟上升沿由次态赋予现态而出现时计数器的值仍为0到下一个时钟周期的上升沿计数器检测到下落状态计数值加1如此推想到第20个下落状态出现时计数值为19表示计数器已经记录了20个下落周期。此时是次态转变的关键点若在此时ground 1则下落周期满足 20 cycles次态变为地面状态LEFT或RIGHT若在此时ground 0则下一个时钟周期旅鼠仍处于下落状态计数器值达到20表示计数器已经记录了21个下落周期满足 20 cycles自此以后无论旅鼠再保持下落状态多少周期一旦接触地面ground 1就将在下个时钟周期上升沿进入死亡状态SPLAT。以上描述看似已经解决了这个问题但是实际运行发现如果将计数寄存器设置为reg [4:0] cnt则编译仿真后结果不正确。这是因为当前的设计只关注了逻辑设计的正确性而忽略了实际硬件电路的运行需求。想当然地认为只要设置计数器寄存器能容纳大于20的数比如21就可以代表满足旅鼠进入死亡的条件因此设置5位宽可表示5’d0~5’d31的寄存器就够了。实际上我们应该关注题目中的下面这句话。There is no upper limit on how far a Lemming can fall before hitting the ground.也就是说旅鼠落地之前并不仅仅在空中停留满足“落地成盒”的最小时钟周期数21即保持21个周期的下落状态就接触地面并死亡而是可能处于下落状态更久才接触地面即保持nn 21个时钟周期下落状态后ground 1这就可能导致寄存器溢出这就延伸出了两种解决方法一种就是上述代码通过逐步加大寄存器的位宽让代码通过样例测试为止看似暴力实际中很多问题就是这样解决的……多给些寄存器带宽让计数值覆盖到所有可能出现的计数值即可不必重新优化逻辑。出于学习与严谨目的无下落状态周期限制代表无论我们使用多大的计数寄存器都有可能发生溢出可以采用设置标志位的方法该标志位的变化是基于时序逻辑所以在时钟上升沿检测到计数器的值为19时拉高标志位此后只有复位才使其拉低表示此时计数器计数值已经 20cycles自此之后无论何时拉高ground等待小鼠的次态都是SPLAT。这时只要寄存器位宽满足“可计21个数0~20”用来触发timeout信号拉高即可故而恢复reg [4:0] cnt。由此给出最终的Verilog HDL如下moduletop_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);localparam LEFT1,RIGHT2,FALL_L3,FALL_R4,DIG_L5,DIG_R6,SPLAT7;reg[2:0]state;reg[2:0]next_state;reg[4:0]cnt;// [4:0] is enough now.reg timeout;always (posedge clk or posedge areset)if(areset)stateLEFT;elsestatenext_state;always (posedge clk or posedge areset)if(areset)cnt5d0;elseif(stateFALL_L||stateFALL_R)cntcnt1b1;elsecnt5d0;always (posedge clk or posedge areset)if(areset)timeout1b0;elseif(cnt5d19)timeout1b1;elsetimeouttimeout;always (*)case(state)LEFT:next_state~ground?FALL_L:(dig?DIG_L:(bump_left?RIGHT:LEFT));RIGHT:next_state~ground?FALL_R:(dig?DIG_R:(bump_right?LEFT:RIGHT));FALL_L:next_stateground?(timeout?SPLAT:LEFT):FALL_L;FALL_R:next_stateground?(timeout?SPLAT:RIGHT):FALL_R;DIG_L:next_state~ground?FALL_L:DIG_L;DIG_R:next_state~ground?FALL_R:DIG_R;SPLAT:next_stateSPLAT;endcase assign walk_left(stateLEFT);assign walk_right(stateRIGHT);assign aaah(stateFALL_L)||(stateFALL_R);assign digging(stateDIG_L)||(stateDIG_R);endmodule总结遇到一时理不清逻辑的电路一定要善加利用时序图无论是手绘还是仿真软件。至此HDLBits旅鼠系列完结

更多文章