• 硬件语言编写规范与技巧


      重要:总之一句话,与外部信号通信时一定要多一个心眼。

      在下面的讲解中,你会发现4、6小节都用了双重甚至是三重的always语句。那是因为此时接收的数据是异步的,通过多重always语句可以避免冒险竞争,可以同步时钟信号。

      也许你也可以看出在1小节中,我们用了双重的“<=”赋值语句,其目的是检测到wr_state信号的变化,因为是外部信号,即使是存入寄存器也还是不太稳定,所以我们就把数据传给wr1和wr2两个寄存器,这样就稳定了,然后比较前后,就可以知道变化了。

      1、那么当我们想知道寄存器的变化情况该怎么办?

        可以用下列语句:

          wire wr_state = mcu_cs_n || mcu_wr_n;  //外部信号

          always@(posedge clk or negedge rst_n) begin

            wr1<=wr_state;

            wr2<=wr1;

            end

          wire pos_wr=~wr2&&wr1;  //这样就可以间接地检测到寄存器的变化了。

     

      2、我们如何检测变化?

        wire wr_state=mcu_cs_n||mcu_wr_n;  //同时都是低电平的时候

        wire pos_wr=~wr2&&wr1;  //这是很常见的对一个信号稳定后的检测

        与 或 非 都可以进行想要的处理。

      3、关于数据是否稳定的问题?

        且看下面的语句:

          always@(posedge clk or negedge rst_n) begin

            if(wr_state)   //这里里可以检测到变化

              mcu_addr_r<=mcu_addr;  //地址锁存

              mcu_db_r<=mcu_db;  //数据锁存

            end

         分析:这里在检测到wr_state变化时,就马上锁存数据,那么,总线(mcu_addr和mcu_db)上的数据此时是否稳定呢,不可而知。所以比较保险的做法,就是延迟一两个时钟周期,再锁存数据。

      4、那么问题来了,我们如何延迟一两个时钟周期呢?

          always@(posedge clk or negedge rst_n) begin

            if(!rst_n) begin

              reqr1<=1'b1;

              reqr2<=1'b1;

              reqr3<=1'b1;

            end

            else begin

              reqr1<=req;

              reqr2<=req1;

              req3<=req2;

            end

          wire pos_req1=reqr1&~reqr2;  //延迟一个时钟周期

          wire pos_req2=reqr2&~reqr3;  //延迟两个时钟周期

        分析:

          这种写法不仅可以检测到req的数据变化,还可以检测到延迟的数据变化,还可以随意延迟N个时钟周期,在延迟期间,其他数据就得以稳定。

      5、如何看待程序中的硬件连接? 

        可以用下列图示,表示硬件连接。

     

      6、不可在两个always语句中对同一个寄存器赋值,如何破解?

        可以申请两个寄存器。

        ***************************************

        reg key_rst;

        always @(posedge clk or negedge rst_n)
          if (!rst_n) key_rst <= 3'b111;
          else key_rst <= {sw3_n,sw2_n,sw1_n};

        ***************************************

        reg low_sw;

        always @(posedge clk or negedge rst_n)
          if (!rst_n) low_sw <= 3'b111;
          else if (cnt == 20'hfffff) 
          low_sw <= {sw3_n,sw2_n,sw1_n};

         ***************************************

        wire[2:0] key = key_rst & (~low_sw);

      7、编写程序要从硬件连接的角度去思考

        如第7要点,对于同一个按键,我们要看到的是下面的硬件连接。

        话说只有这样的硬件连接才看着舒服些,如果是接着在key_rst后面在获取按键的值就有些不妥了,因为key_rst后面已经有了一些组合逻辑的的门电路,本身就比较复杂了。

      8、输出可以是reg寄存器吗?不可以,最好是wire型。

        reg就是用always语句处理,wire就是用assign语句处理,所以通常是assign语句作为输出,如下代码:

        reg d1;
        reg d2;
        reg d3;

        always @ (posedge clk or negedge rst_n)
          if (!rst_n) begin
            d1 <= 1'b0;
            d2 <= 1'b0;
            d3 <= 1'b0;
          end
          else begin //某个按键值变化时,LED将做亮灭翻转
            if ( led_ctrl[0] ) d1 <= ~d1;   //这里不可以直接用led_d3<=led_ctrl,因为led不是reg型,况且,输出不能是寄存器,只能是wire型
            if ( led_ctrl[1] ) d2 <= ~d2;
            if ( led_ctrl[2] ) d3 <= ~d3;
          end

        assign led_d3 = d1 ? 1'b1 : 1'b0; //LED翻转输出
        assign led_d2 = d2 ? 1'b1 : 1'b0;
        assign led_d1 = d3 ? 1'b1 : 1'b0;

       9、同一个寄存器不可以在两个always语句中赋值,但是可以在两个语句中判断该寄存器。

        reg led_dir;
        reg led_on;
        always@(posedge clk or negedge rst_n)
          if(!rst_n) begin
            led_dir<=1'b1;
            led_on<=1;
            end
          else if(key_r_later[0])//按键0---LED开关
            led_on<=~led_on;
          else if(key_r_later[1])
            led_dir<=1'b1;
          else
            led_dir<=1'b0;

        //--------------------------------------------

        always@(posedge clk or negedge rst_n)
          if(!rst_n) begin
            led_r<=4'b0001;
            end
          else if(cnt==24'hffffff&&led_on) begin
            if(led_dir)
              led_r<={led_r[2:0],led_r[3]};
            else
              led_r<={led_r[0],led_r[3:1]};
            end

      10、对于input类型的端口  

        我们没法改变其原值。因为他是由外部输入决定的,因此,我们不可以在对其赋值。我没只能是读取其值。

      11、可以滤波的一种写法

        由于FPGA的周期很短,全频率很高,计算速度很快,这些有可能在检测的时候引进一些干扰波。怎样滤除这些干扰,下面就是一种方法。

        always @ (posedge clk or negedge rst_n)

          begin
            if(!rst_n)

              begin
                rs232_rx0 <= 1'b0;
                rs232_rx1 <= 1'b0;
                rs232_rx2 <= 1'b0;
                rs232_rx3 <= 1'b0;
              end
            else

              begin
                rs232_rx0 <= rs232_rx;
                rs232_rx1 <= rs232_rx0;
                rs232_rx2 <= rs232_rx1;
                rs232_rx3 <= rs232_rx2;
              end
          end
        //下面的下降沿检测可以滤掉<20ns-40ns的毛刺(包括高脉冲和低脉冲毛刺)
        assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0; 

        // 0123

        // 0000------0             0111-------0

        // 1000------0             0011-------1

        // 1100------0   ---->  0001-------0

        // 1110------0             0000-------0

        // 1111------0             1000-------0

      11、如何一次性输出所有数组寄存器的数据?

         if(num == 4'd12)  //到这里是一起都传递过去

          begin 
            num <= 4'd0; 
            rx_data_r <= rx_temp_data; //但是我们不能在这里传递,所以需要一个寄存器过渡。
          end

        assign  rx_data = rx_data_r;

      12、模块中有两种输入,第一种是clk和rst_n;第二种是其他引脚输入,如 bps_clk,我们如何应对?

        对于第一种情况:

          reg num;  //自己创建寄存器

          always@(posedge clk or negedge rst_n)

            if(!rst_n)

              num<=1'b0;

            else

              num<=num+1'b1;  //这里对寄存器赋值

        对于第二种情况:

          input num;

          reg num;  //哎!实事求是吧,这也是其中一种情况。。。。。。

          always@(posedge clk or negedge rst_n)

            if(!rst_n)

              num<=1'b0;

            else

              num<=num+1'b1;  //这里对寄存器赋值

       13、与FPGA外部端口的连接,如何设置???

        FPGA与外部信号的链接,是一对一的,与端口传输的是多少bit无关。

        例如:

          module ps2_top(clk,rst_n,ps2k_clk,ps2k_data,rs232_tx);

          input clk,rst_n;

          input ps2k_clk;

          input ps2k_data; //这里传输的8bit数据,但是我们秉持一对一的原则。

          output rs232_tx;//这是传输1bit数据

        

          reg[7:0] ps2k_data_r;//重新定义一个寄存器,用来储存8比特数据。

          endmodule

      14、注意的是在不同的if下,往往会产生不同的结果,一定要想清楚。

        反正是注意就行了,慢慢就熟练了。

        else if(num==4'd10)//接收完毕
          begin
            if(ps2_byte_r==8'hf0)//如果是松开按键
            key_f0<=1'b1;//松开标志位为1,说明是无效数据
          else //如果是按下按键
            begin
            if(!key_f0)//如果是0(说明这是按下的键,应该接收)
              begin //说明有键按下
              ps2_int_r<=1'b1;//开始传输
              ps2_dyte_r1<=ps2_byte_r; //锁存当前键值
              end
            else //如果是1(说明这是松开的按键,我们就不用接收)
              begin
              ps2_int_r<=1'b0;
              key_f0<=1'b0;
              end
            end
          end

  • 相关阅读:
    【CentOS】CentOS7开放及查看端口
    【nginx】配置https 证书生成的方法
    【MacOs】 Royal TSX SSH 和 FTP 中文乱码
    【PHP】thinkphp3.2.5
    【TCP/IP】入门学习笔记 五
    【TCP/IP】入门学习笔记 四
    HTTP
    【PHP】高并发和大流量的解决方案(思路)
    react多级路由 重定向与404定义
    react自定义导航组件 路由参数
  • 原文地址:https://www.cnblogs.com/qidaiymm/p/4969868.html
Copyright © 2020-2023  润新知