FPGA学习笔记之流水灯(1)

FPGA学习笔记之流水灯(1)

FPGA的开发上手并不难,但是想成为顶尖高手十分不易,不知道要流过多少汗水,写过多少万行代码,调试过多少板子。但是我们不能因为山高难爬就放弃攀登,不能因为美人难追就放弃追求吧,况且学习的过程中也有诸多乐趣。现在就从大家最常接触的流水灯开始入手学习FPGA的开发,将实现多种样子的流水灯。开发语言采用Verilog。

  • 功能描述
  • 代码
  • 总结

功能描述

有编号1、2、3、4共4个LED灯,实现1灯至4灯的循环点亮,每次点亮1个LED灯。复位时所有灯全亮。

代码

下面代码将采用多种方式实现同等功能。

思路1:采用if语句实现。为使代码直观明了,使用两个always块,一个用于计时,一个根据所计时间判断该第几个灯点亮。

module led_top(
     input           clk,             //系统时钟,50MHz
     input           rst_n,           //复位
     output reg[3:0] led              //输出
    );

parameter LED_CNT1 = 25_000_000;       //每数0.5秒LED闪一次
parameter LED_CNT2 = LED_CNT1 * 2;
parameter LED_CNT3 = LED_CNT1 * 3;
parameter LED_CNT4 = LED_CNT1 * 4; 

reg [31:0] count;
always @(posedge clk or negedge rst_n)  //计数器在0~LED_CNT4-1之间循环
begin
  if(!rst_n)
    count <= 0;
  else if(count == LED_CNT4 -1)
    count <= 0;
  else
    count <= count + 1;
end

always @(posedge clk or negedge rst_n)
begin
  if(!rst_n)
     led <= 4'b1111;
  else if(count == LED_CNT1-1)
     led <= 4'b0001;
  else if(count == LED_CNT2-1)
     led <= 4'b0010;
  else if(count == LED_CNT3-1)
     led <= 4'b0100;
  else if(count == LED_CNT4-1)   
     led <= 4'b1000;   
end

endmodule

思路2:采用case语句实现。定义5种状态,分别对应复位时灯全亮、第一支灯亮、第二支灯亮、第三支灯亮、第四支灯亮,根据计数器的指示,进行状态跳转。

module led02_top(
     input           clk,    //FPGA时钟,50MHz
     input           rst_n,  //复位
     output reg[3:0] led     //输出灯
    );

parameter LED_CNT    = 25_000_000;     //即0.5 秒              
parameter state_idle = 5'b00001;
parameter ledstate1  = 5'b00010;
parameter ledstate2  = 5'b00100;
parameter ledstate3  = 5'b01000;
parameter ledstate4  = 5'b10000;

reg [31:0] count;             //时间计数器
reg        led_en;            //跳转指示器,每当其为1时,赋值给下一灯
reg [4:0]  led_state;         //当前状态

always @(posedge clk or negedge rst_n)  //计数器在0~LED_CNT4 -1之间循环
begin
  if(!rst_n)
    begin
        count   <= 0;
        led_en  <= 0;             //指示器为0
    end
  else if(count == LED_CNT-1)
    begin
      count   <= 1;
      led_en  <= 1;               //指示器为1
    end
  else
    begin
      count  <= count + 1;
      led_en  <= 0;               //指示器为0
    end
end

always @(posedge clk or negedge rst_n)    //完成状态跳转
begin
  if(!rst_n)
    begin
      led_state <= state_idle;
    end
  else if(led_en == 1'b1) 
    begin
      case(led_state)
          state_idle: led_state <= ledstate1;
          ledstate1:  led_state <= ledstate2;
          ledstate2:  led_state <= ledstate3;
          ledstate3:  led_state <= ledstate4;
          ledstate4:  led_state <= ledstate1;
          default:    led_state <= state_idle;
      endcase
    end
end

always @(posedge clk or negedge rst_n)   //每种状态对应的LED灯赋值
begin
  if(!rst_n)
    begin
      led <= 4'b1111;
    end
  else
    begin
      case(led_state)
          state_idle:led <= 4'b1111;
          ledstate1: led <= 4'b0001;
          ledstate2: led <= 4'b0010;
          ledstate3: led <= 4'b0100;
          ledstate4: led <= 4'b1000;
          default:   led <= 4'b1111;
       endcase
     end
end 

endmodule

总结

代码1和代码2分别用if…else语句和case语句实现相同的功能。在代码2中,并未严格按三段式状态机来写,这里先以实现功能为主,在后续的学习中再研究三段式状态机的写法。

通常来说,if-else语句生成具有优先级的编码逻辑,速度较慢,占用的面积小,对速度没有要求但对面积有较高要求的情形。case语句生成并行的编码逻辑,速度较快,占用的面积大,适合对速度有较高要求的情形。

你可能感兴趣的:(fpga学习)