Verilog设计JTAG接口与TAP控制器?实现Xilinx FPGA的动态重构与Zynq配置重加载

         根据上一篇文章,我们已经了解到重构 Xilinx FPGA 的方法有两种:Slave Select Map 接口JTAG 接口。在文章中,我们详细探讨了这两种接口的工作原理、应用场景以及它们各自的优缺点。通过对比,我们了解了在不同的开发需求和硬件环境下,如何选择最适合的接口来进行 FPGA 配置和加载。

        在本篇文章中,我们将重点介绍如何通过 Verilog HDL(硬件描述语言) 实现 JTAG 接口的配置逻辑,成功通过外部FPGA加载Xilinx FPGA,并进行下板验证。

 完整工程文件下载:Verilog实现JTAG加载源码 (点击蓝色字体获取)

一、硬件环境

  1. 加载FPGA:zynq7020 zedboard(其他的FPGA也可以)
  2. 被加载FPGA:zynq7020(Xilinx的其他也可以)
  3. 开发软件版本:vivado2019.2
  4. 存储配置文件:外挂FLASH (型号:W25Q128

 

二、JTAG加载时序流程

加载时序主要是通过JTAG时序去控制TAP控制器状态机去发送相应的指令和数据,TAP状态机如下图所示

Xilinx FPGA通过JTAG接口加载PL程序整体加载过程如下:

  1. FPGA上电,且在TEST-LOGIC-RESET状态维持5个sck
  2. 进入SHFIT-IR状态,并且加载JPROGAM指令(LSB first)
  3. 在IDLE状态至少等待10ms,相当于对FPGA的一个初始化时间
  4. 加载CFG_IN指令(LSB first)
  5. 加载bin配置文件(MSB first)
  6. 加载JSTART指令(LSB first)
  7. 在IDLE状态至少等待2000个sck,使配置文件加载成功

通过切换TAP并在脉冲PROGRAM_B引脚或发出关机序列后输入CFG_IN指令,可以重新配置已配置的设备,其时序流程图如下所示

三、Verilog代码实现

        我的设计方案是将FPGA配置文件存储在闪存中,通过SPI接口读取闪存中的数据,并通过JTAG接口将该配置文件传输至另一块FPGA开发板,从而使目标FPGA运行预定的功能。这一过程可以确保配置文件的可靠传输和FPGA功能的灵活部署。

3.1 总体设计框图

总体设计框图如下所示

我这边设置了一个按键触发条件,当用户按下按键后,开始执行读取FLASH内存,并通过JTAG去加载FPGA开发板

3.2 代码设计

代码总体架构,主要由三个模块组成,顶层文件、JTAG时序模块、FLASH读取模块

3.2.1 顶层模块

顶层模块,主要负责将所有子模块、外设和连接进行整合,在设计中使用模块化设计,把各个模块单独出来,并全部例化的顶层中

顶层代码

    key_filter
    #(
        .CNT_MAX    (20'd999_999/6   )   //计数器计数最大值
    )
    key_filter_inst
    (
        .sys_clk    (jtag_clk    ),  //系统时钟,频率10MHz
        .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
        .key_in     (pi_key     ),  //按键输入信号

        .key_flag   (po_key     )   //消抖后信号
    );

    jtag_pl 
    #(
        .BIT_SIZE    (BIT_SIZE + 100 )   //BIT文件大小
    )
    u_jtag_pl
    (
        .clk_in  ( jtag_clk  ),
        .ila_clk  ( ila_clk  ),
        .rst     ( ~sys_rst_n     ),
        .key_begin  (po_key),
        .flash_tdo    ( flash_tdo_out   ),
        .flash_rd_en   ( flash_rd_en   ),
        .led     ( led     ),
        .led1    ( led1    ),
        .led2    ( led2    ),
        .tck_out ( jtag_tck ),
        .tms_out ( jtag_tms ),
        .tdi_out ( jtag_tdi ),
        .tdo_in  ( jtag_tdo )
    );


    //-------------flash_read_ctrl_inst-------------
    flash_read_ctrl  
    #(
        .BIT_SIZE    (BIT_SIZE    )   //读出数据个数
    )
    flash_read_ctrl_inst
    (
        .sys_clk    (clk_10M    ),  //系统时钟,频率50MHz
        .ila_clk    (ila_clk)   ,
        .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
        .key        ( flash_rd_en ),  //读取开始信号
        .miso       (flash_tdo       ),  //读出flash数据

        .sck        (flash_sck        ),  //片选信号
        .cs_n       (flash_cs_n       ),  //串行时钟
        .mosi       (flash_tdi       ),  //主输出从输入数据
        .tx_flag    (tx_flag    ),  //输出数据标志信号
        .tx_data    (tx_data    )   //输出数据
    );

3.2.2 FLASH读取模块

FLASH读取模块主要采用SPI 模式0方式对W25Q128器件,进行x1方式读取,读到的数据输出为单字节的格式,输出给JTAG加载模块

读FLASH代码

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == READ) && (cnt_byte>= 16'd4))
        mosi    <=  1'b0;
    else    if((state == READ) && (cnt_byte == 16'd0) && (cnt_sck == 2'd0))
        mosi    <=  READ_INST[7 - cnt_bit];  //读指令
    else    if((state == READ) && (cnt_byte == 16'd1) && (cnt_sck == 2'd0))
        mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
    else    if((state == READ) && (cnt_byte == 16'd2) && (cnt_sck == 2'd0))
        mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
    else    if((state == READ) && (cnt_byte == 16'd3) && (cnt_sck == 2'd0))
        mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址

//miso_flag:miso提取标志信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        miso_flag   <=  1'b0;
    else    if((cnt_byte >= 16'd4) && (cnt_sck == 2'd1))
        miso_flag   <=  1'b1;
    else
        miso_flag   <=  1'b0;

3.2.3 JTAG驱动模块

此模块主要通过JTAGIEEE 1149.1标准协议,对TAP控制器发送相应的指令,并把从FLASH中读出到的数据通过协议发送出去。

JTAG驱动代码

module jtag_pl
#(
    parameter   BIT_SIZE    =   32'd100    //bit文件大小
)
(
    input clk_in, //Y9
    input ila_clk,
    input rst,  //T18

    input key_begin,
    input flash_tdo,
    output  reg flash_rd_en,

    output reg led, //T22
    output reg led1,//T21
    output reg led2,//U22
    output tck_out, //AA9
    output tms_out, //Y10
    output tdi_out, //Y11
    input  tdo_in  //AA11   
);
    wire tck;

    reg[7:0]    count2;
    reg[7:0]    count5;
    reg[15:0]   count2000;
    reg[32:0]   count10000;
    reg[7:0]    state;
    reg         tms;
    reg         tdi;
    reg[8:0]    jprogram;
    reg[8:0]    cfg_in;
    reg[8:0]    jstart;

然后给工程分配好引脚,编译综合得到bit文件

 

四、硬件连接

硬件连接主要是通过杜邦线连接zedboard任意IO口到被加载FPGA开发板,然后通过任意IO在外接一个FLASH,用来存储bin文件

五、测试前准备

首先建立一个被加载FPGA开发板的工程,我这边是设计了一个点灯的程序,首先通过Xilinx官方的下载器,下载到开发板,先看一下效果

这样可以确定加载的bit文件没问题,然后在vivado选择生成.bin文件,把生成的bin文件存储到FLASH中,供后续加载使用

 

六、下板测试

给加载FPGA开发板插上下载器,然后通过杜邦线连接被加载FPGA JTAG接口

给两个开发板都上电,先把第三步生成的bit文件烧写到加载FPGA开发板,然后通过按键去触发,程序加载,完整流程如下所示

由上视频可以看出,JTAG加载速度非常快,而且很稳定

这样咱们就大功告成了!!!成功通过一个开发板去加载另外一个开发板。

这次仅仅测试加载了ZYNQ的PL端,但是他完全适用于A7、K7等Xilinx的纯FPGA开发板,只需要在FLASH中bin文件进行修改即可。

后续我将继续给大家讲解,如何通过JTAG给ZYNQ加载PS端程序以及加载操作系统到ZYNQ。

 

制作不易,记得三连哦,给我动力,持续更新!!!

posted @ 2024-12-20 13:36  FPGAmaster  阅读(874)  评论(0)    收藏  举报