• Verilog实战学习到RiscV - 4 : ICEStick 评估板计数器


    这篇是关于always 时序逻辑的。直接上代码。

    引脚配置文件

    set_io  leds[0]  99
    set_io  leds[1]  98
    set_io  leds[2]  97
    set_io  leds[3]  96
    
    set_io  -pullup yes pmod[0]  78
    set_io  -pullup yes pmod[1]  79
    

    参看icestick的原理图
    请添加图片描述

    这里在pmod上使用了内部的上拉电阻

    代码

    module top_counter (
        input [1:0] pmod,  // 对应icestick IO
        output reg [3:0] leds   // reg: 综合工具Yosys会将leds连接到D-FF
    );
        wire clk;
        wire rst;
        assign clk = ~pmod[0];
        assign rst = ~pmod[1];
    
        always @(posedge clk or posedge rst) begin
            if (rst == 1'b1) begin
                leds <= 4'b0000;
            end 
            
            if (clk == 1'b1) begin
                leds <= leds + 1'b1;
            end
    
        end
    endmodule
    

    这段时序逻辑电路在使用Yosys 综合的时候产生了如下错误:

    Creating register for signal `\SB_DFFES.\Q' using process `\SB_DFFES.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:803$203'.
      created $adff cell `$procdff$447' with positive edge clock and positive level reset.
    Creating register for signal `\SB_DFFESS.\Q' using process `\SB_DFFESS.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:742$196'.
      created $dff cell `$procdff$448' with positive edge clock.
    Creating register for signal `\SB_DFFER.\Q' using process `\SB_DFFER.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:662$192'.
      created $adff cell `$procdff$449' with positive edge clock and positive level reset.
    Creating register for signal `\SB_DFFESR.\Q' using process `\SB_DFFESR.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:601$185'.
      created $dff cell `$procdff$450' with positive edge clock.
    Creating register for signal `\SB_DFFS.\Q' using process `\SB_DFFS.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:527$182'.
      created $adff cell `$procdff$451' with positive edge clock and positive level reset.
    Creating register for signal `\SB_DFFSS.\Q' using process `\SB_DFFSS.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:477$179'.
      created $dff cell `$procdff$452' with positive edge clock.
    Creating register for signal `\SB_DFFR.\Q' using process `\SB_DFFR.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:406$176'.
      created $adff cell `$procdff$453' with positive edge clock and positive level reset.
    Creating register for signal `\SB_DFFSR.\Q' using process `\SB_DFFSR.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:356$173'.
      created $dff cell `$procdff$454' with positive edge clock.
    Creating register for signal `\SB_DFFE.\Q' using process `\SB_DFFE.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:311$171'.
      created $dff cell `$procdff$455' with positive edge clock.
    Creating register for signal `\SB_DFF.\Q' using process `\SB_DFF.$proc$/usr/local/bin/../share/yosys/ice40/cells_sim.v:271$169'.
      created $dff cell `$procdff$456' with positive edge clock.
    Creating register for signal `\top_counter.\leds' using process `\top_counter.$proc$top_counter.v:13$383'.
    ERROR: Multiple edge sensitive events found for this signal!
    make: *** [Makefile:23: top_counter.json] Error 1
    
    

    两个if语句处理两种情况没有问题啊? 但其实,这是一个典型的C语言嵌入式程序猿会犯的典型错误。以下详细解释。

    时钟和复位信号的处理

    在时序逻辑设计中,always块的触发条件决定了什么时候执行其中的逻辑。在Verilog代码中,我们需要处理两个信号:

    时钟信号(clk):通常用于在每个时钟周期(上升沿或下降沿)更新状态。
    复位信号(rst):通常用于在复位条件下重置状态,一般来说是异步复位,即不依赖时钟。

    为什么不在always块内部检查时钟电平?

    • 冗余:在always块中检查clk == 1’b1是多余的,因为我们已经在触发条件中指定了posedge clk,这意味着我们只在clk上升沿时执行代码。在执行代码时,clk必然处于高电平,因此再检查clk的电平是多余的。

    • 潜在错误:在时序逻辑中直接检查时钟的电平可能导致不一致或错误的行为,特别是在综合工具和仿真环境中。

    这就解释了Yosys 所报的错误:

    ERROR: Multiple edge sensitive events found for this signal!
    

    正确的设计方式

    在时序逻辑设计中,我们不应该在always块内部检查时钟信号的电平(例如clk == 1’b1),因为我们已经在always块的触发条件中指定了对时钟上升沿的响应。对于复位信号,一般我们会处理为同步或异步复位,这取决于设计要求。代码中已经指定了posedge rst,这通常表示异步复位。

    修正后的always块

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            leds <= 4'b0000; // 异步复位
        end else begin
            leds <= leds + 1'b1; // 时钟上升沿时计数
        end
    end
    
    1. 触发条件:
    • always @(posedge clk or posedge rst)表示每当clk上升沿或rst上升沿时,这个块中的代码会被执行。
    • 触发条件是“边沿触发”(edge-sensitive),即代码只会在信号的边沿(上升或下降)发生变化时执行,而不会响应信号的电平状态。
    1. 复位处理:
    • 在块的开头,我们首先检查复位信号rst是否为高电平(有效),如果是,则将leds重置为4’b0000。
    • 这里的复位是异步的,因为复位发生时不需要等待时钟上升沿,只要rst变为高电平就立即重置。
    1. 计数逻辑:
    • 如果复位信号不为高电平(即rst无效),那么在时钟的上升沿,leds会递增1。
    • 这里的计数逻辑是同步的,因为计数操作仅在时钟的上升沿进行。

    Makefile

    上篇一条条输入命令有点麻烦,这次我写了一个 Makefile 方便很多。

    # Define the top-level module and output files
    TOP = top_counter
    BLIF = top_counter.blif
    JSON = top_counter.json
    ASC = top_counter.asc
    BIN = top_counter.bin
    PCF = pinmap.pcf
    
    # Define the Yosys, nextpnr, and icestorm commands
    YOSYS_CMD = yosys -p "synth_ice40 -top $(TOP) -blif $(BLIF) -json $(JSON)" $(TOP).v
    NEXTPNR_CMD = nextpnr-ice40 --hx1k --json $(JSON) --pcf $(PCF) --asc $(ASC)
    ICEPACK_CMD = icepack $(ASC) $(BIN)
    ICETIME_CMD = icetime -tmd hx1k $(ASC)
    ICEPROG_CMD = iceprog $(BIN)
    
    # Default target
    all: $(BIN)
    
    # Yosys synthesis
    $(BLIF) $(JSON): $(TOP).v
    	$(YOSYS_CMD)
    
    # nextpnr place and route 
    $(ASC): $(JSON) $(PCF)
    	$(NEXTPNR_CMD)
    
    # Icepack to create a binary file
    $(BIN): $(ASC)
    	$(ICEPACK_CMD)
    
    # Timing analysis (optional)
    timing: $(ASC)
    	icetime -tmd hx1k $(ASC)
    
    # Program the FPGA
    program: $(BIN)
    	iceprog $(BIN)
    
    # Clean up
    clean:
    	rm -f $(BLIF) $(JSON) $(ASC) $(BIN)
    
    .PHONY: all clean timing program
    

    烧写

    $ iceprog top_counter.bin 
    init..
    cdone: high
    reset..
    cdone: low
    flash ID: 0x20 0xBA 0x16 0x10 0x00 0x00 0x23 0x72 0x21 0x19 0x05 0x00 0x58 0x00 0x21 0x16 0x07 0x17 0xCE 0x6D
    file size: 32220
    erase 64kB sector at 0x000000..
    programming..
    done.                 
    reading..
    VERIFY OK             
    cdone: high
    Bye.
    

    结果

    我没有按钮,就随便用几根线模拟一下按键按下的时候的GND 下降沿和 被内部上拉电阻上拉后的上升沿。

    这里时钟用一根连线模拟,不然时钟跳得太快看不见led变化。后面我们可以做一个分频。

    白线 = CLK
    灰线 = RST

    • Count up
      请添加图片描述

    请添加图片描述

    • Reset
      请添加图片描述
  • 相关阅读:
    Linux入门教程||Linux系统目录结构
    http-response返回数据被截断,返回不完全
    利用Power Automate,轻松下载Power BI报告中的数据
    【openwrt学习笔记】miniupnpd学习笔记
    基于java的博士生信息管理系统
    基于ssm的网上药房管理系统的设计与实现(源码+LW+调试)
    QT基础学习
    数学分析:数项级数的性质
    Ubuntu里安装CMake步骤
    【学习笔记】《Python深度学习》第四章:机器学习基础
  • 原文地址:https://blog.csdn.net/winniezheng/article/details/139560914