AXI单次传输的最大数据是256,超过就会报错。所以大的数据需要分割成多次进行传输。
更新一下python 脚本 数据大于256的时候会自动划分。建议使用。
同时上传一个可以自动生成block design的tcl 脚本。使用的软件是vivado 2019.2, 芯片型号xilinx.com:vcu118:part0:2.0。
如果和我不一样请自己在tcl脚本里面修改。
做SOC原型验证的时候,里面块ROM。存储上电后需要加载的信息。如果能够使用JTAG可以通过电脑动态的更新BRAM,就可以避免重复生成位流。我在xilinx 的IP里发现了一种实现方式。也是第一次使用记录一下。
因为设计AXI总线,所以用block design 例化IP ,可以省去连线的麻烦。
JTAG TO AXI MASTER 模块直接默认。 AXI BRAM CONTROLLER 模块可以根据需要选择BRAM_PORT数量。AXI BRAM CONTROLLER 模块的地址大小无法直接修改,需要在Address Editor中设置Range的大小,然后返回block design 界面validate design 即可应用修改。BRAM_PORTA需要双击设置端口模式,模式和AXI BRAM Controller里面的设置保持一致。
然后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
//创建传输
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.一定要注意。
还有数据中的下划线不是必要的可以省略。
大批量数据处理的时候还是使用python脚本处理比较方便。
附上我用的脚本和tcl控制台内操作的截图;
# 这是一个示例 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()
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()
使用说明,打开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 ""