使用JTAG更新BRAM的方法

更新

2021-12-23_v1

AXI单次传输的最大数据是256,超过就会报错。所以大的数据需要分割成多次进行传输。
更新一下python 脚本 数据大于256的时候会自动划分。建议使用。

同时上传一个可以自动生成block design的tcl 脚本。使用的软件是vivado 2019.2, 芯片型号xilinx.com:vcu118:part0:2.0。
如果和我不一样请自己在tcl脚本里面修改。

引言

做SOC原型验证的时候,里面块ROM。存储上电后需要加载的信息。如果能够使用JTAG可以通过电脑动态的更新BRAM,就可以避免重复生成位流。我在xilinx 的IP里发现了一种实现方式。也是第一次使用记录一下。

文章目录

  • 更新
    • 2021-12-23_v1
  • 引言
  • block design
  • 设计的其他模块
  • JTAG_AXI IP 的使用命令
    • 命令相关
    • 命令的感想
      • 最初的python脚本
      • 2021-12-23_v1更新的python脚本
      • 2021-12-23_v1添加的TCL脚本

block design

因为设计AXI总线,所以用block design 例化IP ,可以省去连线的麻烦。
使用JTAG更新BRAM的方法_第1张图片JTAG TO AXI MASTER 模块直接默认。 AXI BRAM CONTROLLER 模块可以根据需要选择BRAM_PORT数量。AXI BRAM CONTROLLER 模块的地址大小无法直接修改,需要在Address Editor中设置Range的大小,然后返回block design 界面validate design 即可应用修改。BRAM_PORTA需要双击设置端口模式,模式和AXI BRAM Controller里面的设置保持一致。
使用JTAG更新BRAM的方法_第2张图片
然后generate block esign , 可以给block design 在生成一个HDL wrapper, 便于调用。

设计的其他模块

附上顶层代码,top.v

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/12/02 19:53:44
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module top(
    input sys_clk_p,
    input sys_clk_n,
    input rst,
    output wire [7:0] led
    ); 
  clk_wiz_0 sys_pll
   (
    // Clock out ports
    .clk_out_25(clk_out_25),     // output clk_out_25
    .clk_out_100(clk_out_100),     // output clk_out_100
    // Status and control signals
    .reset(rst), // input reset
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1_p(sys_clk_p),    // input clk_in1_p
    .clk_in1_n(sys_clk_n));    // input clk_in1_n
    
  wire [15:0]BRAM_PORTA_addr;
  wire BRAM_PORTA_clk;
  wire [31:0]BRAM_PORTA_din;
  wire [31:0]BRAM_PORTA_dout;
  wire BRAM_PORTA_en;
  wire BRAM_PORTA_rst;
  wire [3:0]BRAM_PORTA_we;
  wire aclk;
  wire aresetn;
  assign aclk = clk_out_25;
  assign aresetn = locked;

  design_1 design_1_i
       (.BRAM_PORTA_addr(BRAM_PORTA_addr),
        .BRAM_PORTA_clk(BRAM_PORTA_clk),
        .BRAM_PORTA_din(BRAM_PORTA_din),
        .BRAM_PORTA_dout(BRAM_PORTA_dout),
        .BRAM_PORTA_en(BRAM_PORTA_en),
        .BRAM_PORTA_rst(BRAM_PORTA_rst),
        .BRAM_PORTA_we(BRAM_PORTA_we),
        .aclk(aclk),
        .aresetn(aresetn));
    wire clkb;
    wire enb;
    wire web;
    wire [13:0] addrb;
    wire [31:0] dinb;
    wire [31:0] doutb;    
blk_mem_gen_0 jtag_bram (
  .clka(BRAM_PORTA_clk),    // input wire clka
  .ena(BRAM_PORTA_en),      // input wire ena
  .wea(BRAM_PORTA_we),      // input wire [0 : 0] wea
  .addra(BRAM_PORTA_addr),  // input wire [13 : 0] addra
  .dina(BRAM_PORTA_din),    // input wire [31 : 0] dina
  .douta(BRAM_PORTA_dout),  // output wire [31 : 0] douta
  .clkb(clkb),    // input wire clkb
  .enb(enb),      // input wire enb
  .web(web),      // input wire [0 : 0] web
  .addrb(addrb),  // input wire [13 : 0] addrb
  .dinb(dinb),    // input wire [31 : 0] dinb
  .doutb(doutb)  // output wire [31 : 0] doutb
);

vio_0 usr_vio (
  .clk(clk_out_25),                // input wire clk
  .probe_in0(doutb),    // input wire [31 : 0] probe_in0
  .probe_out0(clkb),  // output wire [0 : 0] probe_out0
  .probe_out1(enb),  // output wire [0 : 0] probe_out1
  .probe_out2(web),  // output wire [0 : 0] probe_out2
  .probe_out3(addrb),  // output wire [13 : 0] probe_out3
  .probe_out4(dinb)  // output wire [31 : 0] probe_out4
);
  assign led = doutb[7:0]; 
endmodule

JTAG_AXI IP 的使用命令

命令相关

这段主要参考pg174 第19页
使用JTAG更新BRAM的方法_第3张图片

//创建传输
create_hw_axi_txn wr_txn [get_hw_axis hw_axi_1] -address 00000000 -data {11111111_22222222_33333333_44444444_55555555_66666666_77777777_88888888} -len 8 -size 32 -type write
//运行命令
run_hw_axi wr_txn

他的这个数据是反向的,例如上面的命令,实际传输的时候会先传后面的88888888,再传77777777.一定要注意。
还有数据中的下划线不是必要的可以省略。
使用JTAG更新BRAM的方法_第4张图片
使用JTAG更新BRAM的方法_第5张图片

命令的感想

大批量数据处理的时候还是使用python脚本处理比较方便。
附上我用的脚本和tcl控制台内操作的截图;
在这里插入图片描述

最初的python脚本

# 这是一个示例 Python 脚本。

# 按 Shift+F10 执行或将其替换为您的代码。
# 按 双击 Shift 在所有地方搜索类、文件、工具窗口、操作和设置。
import re
import logging


def main(rom_file='wdog_address.elf.rom.rmh', target_file='jtag_bram.tcl'):
    with open(rom_file, 'r') as f1:
        f1_lines = f1.readlines()
        data_len = len(f1_lines)
        f1_lines.reverse()
        f1_lines = re.sub(r'\n', '_', ''.join(f1_lines))
        f1_lines = f1_lines[:-1]
        tcl_create_txn = 'create_hw_axi_txn wr_txn [get_hw_axis hw_axi_1] -force -address 00000000 -data {{{0}}} -len {1} -size 8 -type write\n'.format(
            f1_lines, data_len)
        logging.info('rom_tcl 已经创建好')
        # next create 0 run
        data_len = 128
        data = ''
        for i in range(data_len):
            data = data + '00000000'
            if i != data_len - 1:
                data = data + '_'
        tcl_create_0_txn = 'create_hw_axi_txn wr_0_txn [get_hw_axis hw_axi_1] -force -address 00000000 -data {{{0}}} -len {1} -size 8 -type write\n'.format(
            data, data_len)
        logging.info('0_tcl 已经创建好')

        # 创建提示tcl
        help_str = 'puts {"You can use "run_hw_axi wr_txn" to program JTAG BRAM"}'
        with open(target_file, 'w') as f2:
            f2.write(tcl_create_txn)
            f2.write(tcl_create_0_txn)
            f2.write(help_str)
        logging.info('tcl 文件已经写入成功!')


if __name__ == '__main__':
    LOG_FORMAT = "%(asctime)s - %(levelname)s : [%(lineno)d] %(message)s"
    DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

    logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT)
    main()

2021-12-23_v1更新的python脚本

AXI单次传输的最大数据是256,超过就会报错。所以大的数据需要分割成多次进行传输。
更新一下python 脚本 数据大于256的时候会自动划分。建议使用。

使用说明:运行后会生成三个tcl脚本,data_jtag.tcl clear_jtag.tcl wr_jtag.tcl
使用的时候首先source data_jtag.tcl 创建传输
然后source clear_jtag.tcl 对mem全部写0
最后source wr_jtag.tcl 写数据到mem

# 这是一个示例 Python 脚本。

# 按 Shift+F10 执行或将其替换为您的代码。
# 按 双击 Shift 在所有地方搜索类、文件、工具窗口、操作和设置。
import re
import logging


class RomFile:
    def __init__(self, rom_file: str = "wdog_address.elf.rom.rmh", data_tcl_name='data_jtag.tcl', wr_tcl_name = 'wr_jtag.tcl', wr_0_tcl_name='clear_jtag.tcl'):
        self.rom_file_name = rom_file
        self.rom_data_list = []
        self.wr_txn_list = []
        self.wr_txn_len_list = []
        self.tcl_data = []
        self.tcl_0_data = []

        self.data_tcl_name = data_tcl_name
        self.wr_tcl_name = wr_tcl_name
        self.wr_0_tcl_name = wr_0_tcl_name

    def process_file(self):
        with open(self.rom_file_name, 'r') as f1:
            self.rom_data_list = f1.readlines()
            # self.rom_data_list.reverse()
            data_len = len(self.rom_data_list)
            wr_txn_num = data_len // 256 + 1
            logging.info("Rom data len = {}, generate {} wr_txn.".format(data_len, wr_txn_num))
            for i in range(wr_txn_num):
                logging.info("Process wr_txn_list: {}".format(i))
                if i <wr_txn_num:
                    self.wr_txn_list.append(self.rom_data_list[i*256:(i+1)*256])
                    self.wr_txn_list[i].reverse()
                    self.wr_txn_len_list.append(len(self.wr_txn_list[i]))
                    self.wr_txn_list[i] = re.sub(r'\n', '_', ''.join(self.wr_txn_list[i]))
                    self.wr_txn_list[i] = self.wr_txn_list[i][:-1]
                else:
                    self.wr_txn_list.append(self.rom_data_list[i * 256:])
                    self.wr_txn_list[i].reverse()
                    self.wr_txn_len_list.append(len(self.wr_txn_list[i]))
                    self.wr_txn_list[i] = re.sub(r'\n', '_', ''.join(self.wr_txn_list[i]))
                    self.wr_txn_list[i] = self.wr_txn_list[i][:-1]
            logging.info('Finish process wr_txn_list.')

    def get_tcl_data(self):
        for i in range(len(self.wr_txn_list)):
            address = '0x'+hex(i*256*4)[2:].rjust(8,'0')
            line_str = 'create_hw_axi_txn wr_txn_{id} [get_hw_axis hw_axi_1] -force -address {address} -data {{{data}}} -len {len} -size 8 -type write\n'.format(id=i,address=address,data=self.wr_txn_list[i],len=self.wr_txn_len_list[i])
            self.tcl_data.append(line_str)

            data_len = 256
            data = ''
            for j in range(data_len):
                data = data + '00000000'
                if j != data_len - 1:
                    data = data + '_'
            line_0_str = 'create_hw_axi_txn wr_0_txn_{id} [get_hw_axis hw_axi_1] -force -address {address} -data {{{data}}} -len {len} -size 8 -type write\n'.format(id=i,address=address,data=data,len=data_len)
            self.tcl_0_data.append(line_0_str)

    def write_tcl(self):
        with open(self.data_tcl_name,'w') as f2:
            for line in self.tcl_data:
                f2.write(line)
            for line in self.tcl_0_data:
                f2.write(line)
        with open(self.wr_tcl_name,'w') as f3:
            for i in range(len(self.tcl_data)):
                line_str = 'run_hw_axi wr_txn_{}\n'.format(i)
                f3.write(line_str)
        with open(self.wr_0_tcl_name,'w') as f3:
            for i in range(len(self.tcl_0_data)):
                line_str = 'run_hw_axi wr_0_txn_{}\n'.format(i)
                f3.write(line_str)
        logging.info('Finish!')



def main(rom_file='wdog_address.elf.rom.rmh', target_file='jtag_bram_uart_get_char.tcl'):
    with open(rom_file, 'r') as f1:
        f1_lines = f1.readlines()
        data_len = len(f1_lines)
        f1_lines.reverse()
        f1_lines = re.sub(r'\n', '_', ''.join(f1_lines))
        f1_lines = f1_lines[:-1]
        tcl_create_txn = 'create_hw_axi_txn wr_txn [get_hw_axis hw_axi_1] -force -address 00000000 -data {{{0}}} -len {1} -size 8 -type write\n'.format(
            f1_lines, data_len)
        logging.info('rom_tcl 已经创建好')
        # next create 0 run
        data_len = 256
        data = ''
        for i in range(data_len):
            data = data + '00000000'
            if i != data_len - 1:
                data = data + '_'
        tcl_create_0_txn = 'create_hw_axi_txn wr_0_txn [get_hw_axis hw_axi_1] -force -address 00000000 -data {{{0}}} -len {1} -size 8 -type write\n'.format(
            data, data_len)
        logging.info('0_tcl 已经创建好')

        # 创建提示tcl
        help_str = 'puts {"You can use "run_hw_axi wr_txn" to program JTAG BRAM"}'
        with open(target_file, 'w') as f2:
            f2.write(tcl_create_txn)
            f2.write(tcl_create_0_txn)
            f2.write(help_str)
        logging.info('tcl 文件已经写入成功!')


if __name__ == '__main__':
    LOG_FORMAT = "%(asctime)s - %(levelname)s : [%(lineno)d] %(message)s"
    DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

    logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT)
    # main()
    rom = RomFile()
    rom.process_file()
    rom.get_tcl_data()
    rom.write_tcl()

2021-12-23_v1添加的TCL脚本

使用说明,打开vivado后在底部的tcl命令行 source create_bd1.tcl 即可。
这个脚本适用于vivado 2019.2, 芯片型号xilinx.com:vcu118:part0:2.0。如果和我不一样请自己在tcl脚本里面修改。


################################################################
# This is a generated script based on design: design_1
#
# Though there are limitations about the generated script,
# the main purpose of this utility is to make learning
# IP Integrator Tcl commands easier.
################################################################

namespace eval _tcl {
proc get_script_folder {} {
   set script_path [file normalize [info script]]
   set script_folder [file dirname $script_path]
   return $script_folder
}
}
variable script_folder
set script_folder [_tcl::get_script_folder]

################################################################
# Check if script is running in correct Vivado version.
################################################################
set scripts_vivado_version 2019.2
set current_vivado_version [version -short]

if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
   puts ""
   catch {common::send_msg_id "BD_TCL-109" "ERROR" "This script was generated using Vivado <$scripts_vivado_version> and is being run in <$current_vivado_version> of Vivado. Please run the script in Vivado <$scripts_vivado_version> then open the design in Vivado <$current_vivado_version>. Upgrade the design by running \"Tools => Report => Report IP Status...\", then run write_bd_tcl to create an updated script."}

   return 1
}

################################################################
# START
################################################################

# To test this script, run the following commands from Vivado Tcl console:
# source design_1_script.tcl

# If there is no project opened, this script will create a
# project, but make sure you do not have an existing project
# <./myproj/project_1.xpr> in the current working folder.

set list_projs [get_projects -quiet]
if { $list_projs eq "" } {
   create_project project_1 myproj -part xcvu9p-flga2104-2L-e
   set_property BOARD_PART xilinx.com:vcu118:part0:2.0 [current_project]
}


# CHANGE DESIGN NAME HERE
variable design_name
set design_name design_1

# If you do not already have an existing IP Integrator design open,
# you can create a design using the following command:
#    create_bd_design $design_name

# Creating design if needed
set errMsg ""
set nRet 0

set cur_design [current_bd_design -quiet]
set list_cells [get_bd_cells -quiet]

if { ${design_name} eq "" } {
   # USE CASES:
   #    1) Design_name not set

   set errMsg "Please set the variable  to a non-empty value."
   set nRet 1

} elseif { ${cur_design} ne "" && ${list_cells} eq "" } {
   # USE CASES:
   #    2): Current design opened AND is empty AND names same.
   #    3): Current design opened AND is empty AND names diff; design_name NOT in project.
   #    4): Current design opened AND is empty AND names diff; design_name exists in project.

   if { $cur_design ne $design_name } {
      common::send_msg_id "BD_TCL-001" "INFO" "Changing value of  from <$design_name> to <$cur_design> since current design is empty."
      set design_name [get_property NAME $cur_design]
   }
   common::send_msg_id "BD_TCL-002" "INFO" "Constructing design in IPI design <$cur_design>..."

} elseif { ${cur_design} ne "" && $list_cells ne "" && $cur_design eq $design_name } {
   # USE CASES:
   #    5) Current design opened AND has components AND same names.

   set errMsg "Design <$design_name> already exists in your project, please set the variable  to another value."
   set nRet 1
} elseif { [get_files -quiet ${design_name}.bd] ne "" } {
   # USE CASES: 
   #    6) Current opened design, has components, but diff names, design_name exists in project.
   #    7) No opened design, design_name exists in project.

   set errMsg "Design <$design_name> already exists in your project, please set the variable  to another value."
   set nRet 2

} else {
   # USE CASES:
   #    8) No opened design, design_name not in project.
   #    9) Current opened design, has components, but diff names, design_name not in project.

   common::send_msg_id "BD_TCL-003" "INFO" "Currently there is no design <$design_name> in project, so creating one..."

   create_bd_design $design_name

   common::send_msg_id "BD_TCL-004" "INFO" "Making design <$design_name> as current_bd_design."
   current_bd_design $design_name

}

common::send_msg_id "BD_TCL-005" "INFO" "Currently the variable  is equal to \"$design_name\"."

if { $nRet != 0 } {
   catch {common::send_msg_id "BD_TCL-114" "ERROR" $errMsg}
   return $nRet
}

set bCheckIPsPassed 1
##################################################################
# CHECK IPs
##################################################################
set bCheckIPs 1
if { $bCheckIPs == 1 } {
   set list_check_ips "\ 
xilinx.com:ip:axi_bram_ctrl:4.1\
xilinx.com:ip:jtag_axi:1.2\
"

   set list_ips_missing ""
   common::send_msg_id "BD_TCL-006" "INFO" "Checking if the following IPs exist in the project's IP catalog: $list_check_ips ."

   foreach ip_vlnv $list_check_ips {
      set ip_obj [get_ipdefs -all $ip_vlnv]
      if { $ip_obj eq "" } {
         lappend list_ips_missing $ip_vlnv
      }
   }

   if { $list_ips_missing ne "" } {
      catch {common::send_msg_id "BD_TCL-115" "ERROR" "The following IPs are not found in the IP Catalog:\n  $list_ips_missing\n\nResolution: Please add the repository containing the IP(s) to the project." }
      set bCheckIPsPassed 0
   }

}

if { $bCheckIPsPassed != 1 } {
  common::send_msg_id "BD_TCL-1003" "WARNING" "Will not continue with creation of design due to the error(s) above."
  return 3
}

##################################################################
# DESIGN PROCs
##################################################################



# Procedure to create entire design; Provide argument to make
# procedure reusable. If parentCell is "", will use root.
proc create_root_design { parentCell } {

  variable script_folder
  variable design_name

  if { $parentCell eq "" } {
     set parentCell [get_bd_cells /]
  }

  # Get object for parentCell
  set parentObj [get_bd_cells $parentCell]
  if { $parentObj == "" } {
     catch {common::send_msg_id "BD_TCL-100" "ERROR" "Unable to find parent cell <$parentCell>!"}
     return
  }

  # Make sure parentObj is hier blk
  set parentType [get_property TYPE $parentObj]
  if { $parentType ne "hier" } {
     catch {common::send_msg_id "BD_TCL-101" "ERROR" "Parent <$parentObj> has TYPE = <$parentType>. Expected to be ."}
     return
  }

  # Save current instance; Restore later
  set oldCurInst [current_bd_instance .]

  # Set parent object as current
  current_bd_instance $parentObj


  # Create interface ports
  set BRAM_PORTA [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:bram_rtl:1.0 BRAM_PORTA ]
  set_property -dict [ list \
   CONFIG.MASTER_TYPE {BRAM_CTRL} \
   CONFIG.READ_WRITE_MODE {READ_WRITE} \
   ] $BRAM_PORTA


  # Create ports
  set aclk [ create_bd_port -dir I -type clk -freq_hz 100000000 aclk ]
  set aresetn [ create_bd_port -dir I -type rst aresetn ]

  # Create instance: axi_bram_ctrl_0, and set properties
  set axi_bram_ctrl_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_bram_ctrl:4.1 axi_bram_ctrl_0 ]
  set_property -dict [ list \
   CONFIG.SINGLE_PORT_BRAM {1} \
 ] $axi_bram_ctrl_0

  # Create instance: jtag_axi_0, and set properties
  set jtag_axi_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:jtag_axi:1.2 jtag_axi_0 ]

  # Create interface connections
  connect_bd_intf_net -intf_net axi_bram_ctrl_0_BRAM_PORTA [get_bd_intf_ports BRAM_PORTA] [get_bd_intf_pins axi_bram_ctrl_0/BRAM_PORTA]
  connect_bd_intf_net -intf_net jtag_axi_0_M_AXI [get_bd_intf_pins axi_bram_ctrl_0/S_AXI] [get_bd_intf_pins jtag_axi_0/M_AXI]

  # Create port connections
  connect_bd_net -net Net [get_bd_ports aclk] [get_bd_pins axi_bram_ctrl_0/s_axi_aclk] [get_bd_pins jtag_axi_0/aclk]
  connect_bd_net -net Net1 [get_bd_ports aresetn] [get_bd_pins axi_bram_ctrl_0/s_axi_aresetn] [get_bd_pins jtag_axi_0/aresetn]

  # Create address segments
  assign_bd_address -offset 0xC0000000 -range 0x00010000 -target_address_space [get_bd_addr_spaces jtag_axi_0/Data] [get_bd_addr_segs axi_bram_ctrl_0/S_AXI/Mem0] -force


  # Restore current instance
  current_bd_instance $oldCurInst

  validate_bd_design
  save_bd_design
}
# End of create_root_design()


##################################################################
# MAIN FLOW
##################################################################

create_root_design ""



你可能感兴趣的:(硬件,调试,fpga开发,verilog,经验分享)