FPGA开发--Quartus II常见警告说明及解决方案


文章摘要:
本文对Quartus II中常见的警告说明及解决方案的汇总。


Warning (12241): 2 hierarchies have connectivity warnings - see the Connectivity Checks report folder

处理方法:一般是模块例化接口不匹配,通过Connectivity Checks来查看错误原因

Table of Contents -->
    Analysis & Synthesis -->
        Connectivity Checks

Warning (15714): Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings report for details.

警告(15714):某些引脚具有不完整的I/O分配。 有关详细信息,请参阅“I / O分配警告”报告.

原因说明:此警告主要是因为分配I/O引脚时,采用了默认的参数,主要是用来提醒懒人的,做为良好的设计习惯,一定要仔细对每一个管脚进行计算后,设置合理的参数,而不是简单用个默认值来搪塞;

处理方法::打开Pin Planner,选择current strength和slew rate的参数,将默认值改成非默认值;


Warning (169177): 2 pins must meet Altera requirements for 3.3-, 3.0-, and 2.5-V interfaces. For more information, refer to AN 447: Interfacing Cyclone IV E Devices with 3.3/3.0/2.5-V LVTTL/LVCMOS I/O Systems.

警告(169177):2个引脚必须符合Altera对3.3,3.0和2.5 V接口的要求。 有关更多信息,请参考AN 447:使用3.3 / 3.0 / 2.5-V LVTTL / LVCMOS I / O系统连接Cyclone IV E器件。

原因说明:
这个警告很让人蛋疼,只是针对Cyclone IV E器件,要求设计者注意器件电平匹配的连接方法,而且还不能消除掉,对于不习惯于代码有警告的人来说,每次看到这个警告,心里就会有一万个羊驼呼啸而过......


Warning (35010): Previously generated Fitter netlist for partition "Top" is older than current Synthesis netlist -- using the current Synthesis netlist instead to ensure that the latest source changes are included
Info (35011): Set the option to Ignore source file changes to force the Quartus II software to always use a previously generated Fitter netlist

处理方法:
Assignment-->Design Partition Window-->Netlist Type -->"Source File "

- 阅读全文 -

FPGA开发--基于SPI接口的RTC时钟


文章摘要:
本文主要描述了DS1302实时时钟(RTC,Real-Time Clock)芯片的应用方法;


硬件平台:EP4CE6F17C8
开发环境:Quartus II 13.1


重点内容:
SPI(Master)控制时序;
SPI接口芯片读写时序;
双向IO(inout)的处理(特别重要,以后还要用到);


时序图:

DS1302时序图
CE信号:高电平有效;
写操作:低电平准备,上升沿送出;
读操作:设备在下降沿时送出数据(在上升沿或者下一个下降沿之前读取);
SPI模式: CPOL = 0, CPHA = 0

时钟极性CPOL: 即SPI空闲时,时钟信号SCLK的电平(1:空闲时高电平; 0:空闲时低电平)
时钟相位CPHA: 即SPI在SCLK第几个边沿开始采样(0:第一个边沿开始; 1:第二个边沿开始)


注意事项:

读寄存器操作只有15个时钟,发送字地址结束后的下降沿,DS1302就开始将第一个数据送出,主机应该在下一个下降沿之前读取数据即可;之前因为时序图看的不仔细,这里折腾了很久;


SPI模块:

/*
 *  功能描述:简单的SPI通信模块
 */
module spi_master(
    clk,    // 模块时钟
    rst_n,  // 模块复位
    sclk,   // SPI_CLK
    mosi,   // 主发从收
    miso,   // Master In Slave Out
    
    wdata,  // 待发的数据
    rdata,  // 收到的数据
    wr_req, // SPI发送
    rd_req, // SPI接收
    done,   // SPI操作完成(高电平有效)
    busy    // 繁忙标志 1 - 忙, 0 - 空闲
);

input  clk;
input  rst_n;
output sclk;
input  miso;
output mosi;

input[7:0]  wdata;
output[7:0] rdata;
input wr_req;
input rd_req;
output reg done;
output busy;

reg[7:0] wdata_r;   // 保存待写入的data值
reg[7:0] rdata_r;   // 接收数据寄存器
reg sclk_r;

reg mosi_r;
//--------------------------------------------------

//--------------------------------------------------
// 状态机
parameter S_SPI_IDLE = 4'd0;
parameter S_SPI_SEND = 4'd1;
parameter S_SPI_RECV = 4'd2;

// 500KHz = 2us
parameter SPI_BAUD_CNT = 8'd49;

reg[7:0] mstate;    // 主状态机
reg[7:0] nstate;    // 次状态机

reg[7:0] spi_cnt;   // SPI波特率计时器
reg[4:0] bits;      // 数据位计数
//--------------------------------------------------
assign rdata = rdata_r; 
assign sclk  = sclk_r;
assign mosi  = mosi_r;
assign busy  = (mstate == S_SPI_IDLE) ? 1'b0 : 1'b1;
//--------------------------------------------------
always @(posedge clk or negedge rst_n)
begin
    if(~rst_n) begin
        spi_cnt <= 0;   // 复位  
    end
    
    else if(spi_cnt == SPI_BAUD_CNT) begin    
        spi_cnt <= 0;   // 达到最大值,从新计数
    end
    
    else if(mstate != S_SPI_IDLE) begin    
        spi_cnt <= spi_cnt + 1'b1;  // 非空闲状态才计数   
    end
    
    // 空闲状态,清0计数
    else begin    
        spi_cnt <= 0;       
    end
end
//--------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
        mstate  <= S_SPI_IDLE;    
        done    <= 0;
        sclk_r  <= 0;
    end
    
    else begin
        if(mstate == S_SPI_IDLE) begin
            // 只有空闲时刻才能切换模式
            if(wr_req)
               mstate <= S_SPI_SEND;   
            else if(rd_req)
               mstate <= S_SPI_RECV;
            else
               mstate <= S_SPI_IDLE; 
               
            nstate <= 0;     // 子状态机复位
            done   <= 0;
        end        
        
        else if(mstate == S_SPI_SEND) begin      
                       
            case(nstate)
            0: begin
                nstate <= nstate + 1'b1;     
                bits <= 0;
            end           
            
            1,3,5,7,9,11,13,15: begin
                if(spi_cnt == SPI_BAUD_CNT) begin
                    sclk_r <= 1'b1;               // 上跳发送数据
                    nstate <= nstate + 1'b1;      // 转向下步
                    bits <= bits + 1'b1;
                end
                else begin
                    sclk_r <= 0;  // 保持低电平准备数据
                    mosi_r <= wdata[bits]; // LSB                    
                end                
            end
            
            2,4,6,8,10,12,14,16:begin
                if(spi_cnt == SPI_BAUD_CNT) begin
                    sclk_r <= 1'b0;             // 低电平为下步做准备
                    nstate <= nstate + 1'b1;    // 转向下步
                end
                else begin
                    sclk_r <= 1;  // 保持高电平
                end  
            end
            
            17: begin      
                done   <= 1'b1;
                mstate <= S_SPI_IDLE;   // 操作结束,转向空闲    
            end
            
            default: begin
                mstate <= S_SPI_IDLE;   // 未知状态转向空闲
            end            
            endcase            
        end
        
        // 注意事项:读取时只有15个时钟信号,
        // 发送信号结束后的后边沿(下降沿),时钟芯片已经输出
        else if(mstate == S_SPI_RECV)begin
            case(nstate)
            0:  begin
                nstate <= nstate + 1'b1;
                bits   <= 0;
            end
            1,3,5,7,9,11,13,15: begin
                if(spi_cnt == SPI_BAUD_CNT) begin                    
                    rdata_r[bits] <= miso;  // 上升沿之前读出   
                    bits   <= bits + 1'b1;
                    sclk_r <= 1'b1;                          
                    nstate <=  nstate + 1'b1;                    
                end
                else begin
                    sclk_r <= 1'b0;    // 保持低电平
                end                
            end
            2,4,6,8,10,12,14,16: begin
                if(spi_cnt == SPI_BAUD_CNT) begin
                    nstate <=  nstate + 1'b1;                    
                    sclk_r <= 1'b0;     // 下降沿返回数据 
                end
                else begin
                    sclk_r <= 1'b1;     // 高电平准备数据   
                end
            end                      
            17: begin
                done   <= 1'b1;
                mstate <= S_SPI_IDLE;
            end
            default: begin
                mstate <= S_SPI_IDLE;
            end            
            endcase        
        end
        
        else begin
            mstate <= S_SPI_IDLE;    // 不可识别的命令
        end    
    end
end
//--------------------------------------------------
endmodule

DS1302模块

/*
 *  功能描述: ds1302读写模块
 */
module ds1302(
    clk,    // 模块时钟
    rst_n,  // 模块复位        
    sclk,   // SPI_CLK
    sio,    // SPI_SIO  输出输出复用
    ce,     // 芯片使能脚:高电平有效        
       
    addr,   // 字地址
    wdata,  // 输入的数据  
    rdata,  // 读到的数据    
    reg_wr, // 寄存器写
    reg_rd, // 寄存器读  
    done    // 操作完成标识
);

input  clk;
input  rst_n;
output sclk;
inout  sio;
output ce;
input[7:0]  addr;
input [7:0] wdata;
output[7:0] rdata;
input reg_wr;
input reg_rd;

reg ce_r;
reg spi_rd_req; // SPI读请求
reg spi_wr_req; // SPI写请求
reg [7:0]spi_wdata;

wire [7:0]spi_rdata;
wire spi_done;
//output 
wire busy;
//input en; 
output reg done;

wire mosi;
wire miso;
reg is_out;

reg[7:0] rdata_r;
//--------------------------------------------
assign ce   = ce_r;
assign sio  = is_out? mosi:1'bz; 
assign miso = sio;    
assign rdata = rdata_r;
//--------------------------------------------
reg[31:0] timer;    
//--------------------------------------------
parameter S_IDLE      = 4'd0;
parameter S_READ_CE   = 4'd1;
parameter S_READ      = 4'd2;
parameter S_WRITE_CE  = 4'd5;
parameter S_WRITE     = 4'd3;
parameter S_DONE      = 4'd4;
parameter S_DONE2     = 4'd8;

reg[3:0] state;

parameter BYTE_TIMEOUT = 32'd25_000; // 500us
parameter T1SEC = 32'd50_000_000;    // 1s
parameter T10US = 32'd50_000;        // 1ms
//--------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        state  <= S_IDLE;
        timer  <= 0;
        ce_r   <= 1'b0;
        done   <= 1'b0;
        is_out <= 0;
    end
        
    else begin
        case(state)
        S_IDLE: begin           
            done  <= 1'b0;     
            if(reg_wr) begin
                state <= S_WRITE_CE;       
                ce_r  <= 1'b1;
                is_out<= 1'b1;
                timer <= 0;                
            end    
            else if(reg_rd) begin
                state <= S_READ_CE;       
                ce_r  <= 1'b1;     
                is_out<= 1'b1;
                timer <= 0;
            end
            else begin
                state <= S_IDLE; 
                timer <= 0;   
                ce_r  <= 0;  
                is_out<= 1'b0;
            end
        end
        
        S_READ_CE: begin
            // CE必须保持4us以上
            if(timer == 32'd400) begin            
                spi_wdata <= addr;  // 设置字地址     
                spi_wr_req  <= 1'b1;  // 写使能  
                state     <= S_READ;
                timer     <= 0;
            end
            else 
                timer <= timer + 1'b1;
        end
        
        // 发送字地址
        S_READ: begin
            if(timer == BYTE_TIMEOUT) begin // 超时     
                state <= S_IDLE;  
                timer <= 0;
                is_out<= 1'b0;
            end  
            else if(spi_done) begin  // 发送地址完成    
                is_out    <= 1'b0;   // 设置端口方向为输入
                spi_rd_req  <= 1'b1;   // 启动读使能  
                state     <= S_DONE;                     
                timer     <= 0;  
                               
            end
            else begin
                spi_wr_req  <= 1'b0;
                timer <= timer + 1'b1; 
            end  
        end    
     
        S_WRITE_CE: begin
            // CE必须保持4us以上
            if(timer == 32'd400) begin            
                spi_wdata <= addr;  // 设置字地址     
                spi_wr_req  <= 1'b1;  // 写使能  
                state     <= S_WRITE;
                timer     <= 0;
            end
            else 
                timer <= timer + 1'b1;
        end
        S_WRITE: begin        
            if(timer == BYTE_TIMEOUT) begin   
                state <= S_IDLE;  
                timer <= 0; 
            end              
            else if(spi_done) begin                                     
                spi_wdata <= wdata;  // 设置数据     
                spi_wr_req  <= 1'b1;   // 写使能 
              
                state     <= S_DONE; // 还要进行收尾工作   
                timer     <= 0; 
            end   
            else begin                
                timer   <= timer + 1'b1; 
            end
        end
        
        S_DONE: begin
            if(timer == BYTE_TIMEOUT) begin // 超时     
                state   <= S_IDLE;  
                timer   <= 0;
            end  
            else if(spi_done) begin 
                rdata_r <= spi_rdata;
                state <= S_DONE2; 
                timer <= 0;      
                is_out<= 1'b0;    
            end            
            else begin                
                spi_rd_req  <= 1'b0;  // 撤销读写信号
                spi_wr_req  <= 1'b0;
                timer     <= timer + 1'b1; 
            end    
        end
        
        S_DONE2: begin
            if(timer == 32'd400) begin // 延时8us   
                state   <= S_IDLE;  
                timer   <= 0;
                done    <= 1'b1;       // 操作完成
            end   
            else begin
                ce_r  <= 0;
                timer <= timer + 1'b1;
            end
        end
        
        default: begin
            state <= S_IDLE;
            timer <=0; 
        end    
        endcase
    end

end
//--------------------------------------------
// 例化SPI接口
spi_master spi0(
    .clk(clk),          // 模块时钟
    .rst_n(rst_n),      // 模块复位
    .sclk(sclk),        // SPI_CLK
    .mosi(mosi),        // SPI_SIO    
    .miso(miso),         // 主收
    .wdata(spi_wdata),  // 待发的数据
    .rdata(spi_rdata),  // 收到的数据
    .wr_req(spi_wr_req),
    .rd_req(spi_rd_req),
    .done(spi_done),
    .busy(busy)     // 繁忙标志 1 - 忙, 0 - 空闲
);

endmodule

DS1302模块

- 阅读全文 -

FPGA开发--TimeQuest应用


文章摘要:
本文主要描述了Quartus II 的TimeQuest的应用说明,不做时序分析;

如果编译器会产生以下抱怨:
Critical Warning (332148): Timing requirements not met
则说明工程中未包含时序约束文件,建立约束文件后,则可以消除该警告;


1.新建向导
Assignments --> TimeQuest Timing Analyzer Wizard...
请输入图片描述


2.设置时钟约束
请输入图片描述

Clock Name:自定义一个直观的不重复的即可(后边的约束要用到);
Input Pin:选择对应的输入信号(有下拉菜单可以选择);
Period:时钟周期,50MHz的时钟为20ns;
Rising: 不设置
Falling:不设置


3.输入延时约束

请输入图片描述

PortName: 通过列表选择选择要约束的输入引脚;
tsu: 输入最大时延;
th: 输入最小时延(不设置);
Clock Name: 关联时钟;
Clock Inverted: 时钟反相(下降沿触发需要选择反相)


4.输出延时约束

请输入图片描述

PortName: 通过列表选择选择要约束的输出引脚;
tco: 输出最大时延;
Minimum tco: 输出最小时延(不设置);
Clock Name: 关联时钟;
Clock Inverted: 时钟反相(下降沿触发需要选择反相)


5.Pad-to-Pad约束

一般用于纯组合逻辑电路输入端到输出端之间的约束;

请输入图片描述

Input Port: 组合逻辑输入端;
Output Port:组合逻辑输出端;
tpd: 最大时延;
Minimum tpd: 最小时延;

人为的添加最小时延,以减少或避免竞争和冒险;

组合逻辑电路中,同一信号经不同的路径传输后,到达电路中某一会合点的时间有先有后,这种现象称为逻辑竞争;
因此而产生输出干扰脉冲的现象称为冒险;

- 阅读全文 -

FPGA开发--一种简单的按键处理方法


文章摘要:
本文主要描述了基于FPGA的按键处理的另外一种模式,按照单片机的思路,检测到连续一段时间(20ms)的低电平(按下),视做一次有效按键;


硬件平台: EP4CE6F17C8
开发环境: Quartus II 13.1


示例代码:

/*
 * 功能描述:按键例程,每一次有效按键反转LED显示
 */
module key_test (
    input clk,          // 时钟(50MHz)
    input switch,       // 按键
    output light        // 显示
);

// 50MHz时钟,1us = 50个计数,20ms = 1_000_000个计数;
parameter KEY_SMP_CNT = 32'd1_000_000;

reg[26:0] keys_cnt;    // 计数器用于计算时间
wire keys_flag;        // 有效按键标志

// KEY的值为0,且按下的时间超过20ms,为有效按键
assign keys_flag = ~switch & (keys_cnt == KEY_SMP_CNT);

/*
 * 按键检测,如果按下20ms则视做有效按键
 */
always @(posedge clk)
begin     
    if(switch == 1'b1)
    begin
        keys_cnt <= 0;     // 抬起时重新计数
    end    
    else if(keys_cnt <= KEY_SMP_CNT)
    begin
        // 超出计数后,只再加1就不计数了 
        keys_cnt <= keys_cnt + 1'b1;          
    end
end

// 以下是按键逻辑处理 ----------------------------
reg light_status = 0;

always @(posedge clk)
begin
    if(keys_flag)
    begin
        light_status <= ~light_status;
    end
end           

assign light = light_status;    // 更新LED显示
//-------------------------------------------

endmodule

- 阅读全文 -

FPGA开发--Quartus II使用笔记


文章摘要:
工欲善其事,必先利其器,首先把软件配置好,才能更高效的进行开发工作,本文主要是一些Quartus的使用笔记;


工程清理:
Project --> Clean Project...
选择:All reivsions --> OK


文件备份

Tools-->Options... --> Text Editor
Save backup file项,建议关闭;


缩进指示

Tools-->Options... --> Text Editor
Show indent guide项,建议打开;


制表符

Tools-->Options... --> Text Editor

Tab Size(in spaces): 设置为4;
并选中Insert space on Tab,用空格来替代制表符;

注意事项:必须打开源文件才能对Text Editor进行设置,否则不显示本条目;

- 阅读全文 -


Copyright©2025 春天花会开, All Rights Reserved. Email: webmaster@oroct.com