更新

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 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_nwire [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页

//创建传输
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脚本

# 这是一个示例 Python 脚本。# 按 Shift+F10 执行或将其替换为您的代码。
# 按 双击 Shift 在所有地方搜索类、文件、工具窗口、操作和设置。
import re
import loggingdef 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 rundata_len = 128data = ''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 已经创建好')# 创建提示tclhelp_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 loggingclass 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_fileself.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_nameself.wr_tcl_name = wr_tcl_nameself.wr_0_tcl_name = wr_0_tcl_namedef 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 + 1logging.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 = 256data = ''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 rundata_len = 256data = ''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 已经创建好')# 创建提示tclhelp_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-eset_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 0set cur_design [current_bd_design -quiet]
set list_cells [get_bd_cells -quiet]if { ${design_name} eq "" } {# USE CASES:#    1) Design_name not setset errMsg "Please set the variable <design_name> 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 <design_name> 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 <design_name> 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 <design_name> 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_namecommon::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 <design_name> 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_foldervariable design_nameif { $parentCell eq "" } {set parentCell [get_bd_cells /]}# Get object for parentCellset 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 blkset 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 <hier>."}return}# Save current instance; Restore laterset oldCurInst [current_bd_instance .]# Set parent object as currentcurrent_bd_instance $parentObj# Create interface portsset 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 portsset 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 propertiesset 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 propertiesset jtag_axi_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:jtag_axi:1.2 jtag_axi_0 ]# Create interface connectionsconnect_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 connectionsconnect_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 segmentsassign_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 instancecurrent_bd_instance $oldCurInstvalidate_bd_designsave_bd_design
}
# End of create_root_design()##################################################################
# MAIN FLOW
##################################################################create_root_design ""

使用JTAG更新BRAM的方法相关推荐

  1. 如何学习修改linux系统固件,基于Linux的固件,如何实现更新的好方法?

    我正在使用alix 2d13开发基于linux的设备. 我开发了一个脚本,负责创建映像文件,创建分区,安装引导加载程序(syslinux),内核和initrd,并注意将根文件系统文件放入正确的分区. ...

  2. IE浏览器下ajax缓存导致数据不更新的解决方法

    IE浏览器下ajax缓存导致数据不更新的解决方法 参考文章: (1)IE浏览器下ajax缓存导致数据不更新的解决方法 (2)https://www.cnblogs.com/xcsn/p/4745437 ...

  3. android 异步刷新 方法,android应用中实现异步更新UI的方法有哪些

    android应用中实现异步更新UI的方法有哪些 发布时间:2020-12-07 17:12:00 来源:亿速云 阅读:144 作者:Leah android应用中实现异步更新UI的方法有哪些?相信很 ...

  4. Xlua文件在热更新中调用方法

    Xlua文件在热更新中调用方法 public class news : MonoBehaviour { LuaEnv luaEnv;//定义Lua初始变量 void Awake() { luaEnv ...

  5. linux 本地yum 恢复,Linux_RHEL系统恢复安装光盘中的yum更新源的方法,安装光盘本地YUM更新源挂载安 - phpStudy...

    RHEL系统恢复安装光盘中的yum更新源的方法 安装光盘本地YUM更新源挂载安装光盘: 复制代码代码如下: mount /dev/cdrom /mnt vi /etc/yum.repos.d/loca ...

  6. WPF多线程UI更新——两种方法

    WPF多线程UI更新--两种方法 前言 在WPF中,在使用多线程在后台进行计算限制的异步操作的时候,如果在后台线程中对UI进行了修改,则会出现一个错误:(调用线程无法访问此对象,因为另一个线程拥有该对 ...

  7. mysql插入实现存在更新_mysql 记录不存在时插入 记录存在则更新的实现方法

    mysql 记录不存在时插入在 MySQL 中,插入(insert)一条记录很简单,但是一些特殊应用,在插入记录前,需要检查这条记录是否已经存在,只有当记录不存在时才执行插入操作,本文介绍的就是这个问 ...

  8. update和delete操作忘加where条件导致全表更新的处理方法

    在数据库日常维护中,开发人员是最让人头痛的,很多时候都会由于SQL语句写的有问题导致服务器出问题,导致资源耗尽.最危险的操作就是在做DML操作的时候忘加where条件,导致全表更新,这是作为运维或者D ...

  9. tim怎么设置检测到新版本自动安装 tim安全自动更新的开启方法

    TIM想要开启自动检测新版本并下载安装,该怎么设置呢?下面我们就来看看详细的教程. 1.首先,在你的 电脑中找到TIM; tim怎么设置检测到新版本自动安装?tim安全自动更新的开启方法 2.打开TI ...

最新文章

  1. RDKit | 基于RDKit输出分子结构图(Image)的方法
  2. SQL - SQL 连接 JOIN 例解。(左连接,右连接,全连接,内连接,交叉连接,自连接)[转]...
  3. 浅谈多目标跟踪中的相机运动
  4. boost::detail::yield相关的测试程序
  5. log4j配置使控制台能打印出hibernate生成sql的参数
  6. .NET Core加解密实战系列之——RSA非对称加密算法
  7. C#LeetCode刷题之#389-找不同(Find the Difference)
  8. 前端框架 Angular 11.0.0 正式发布,不再支持 IE 9 、10
  9. java 基本理论知识点
  10. Apache Flink 误用之痛
  11. 性能可靠服务器虚拟化,服务器虚拟化分析
  12. 字符串的迷之算法——KMP,AC自动机,后缀数组
  13. VSCode 代码换行快捷键设置为Shift + Enter
  14. 单片机实例6——报警产生器(硬件电路图+汇编程序+C语言程序)
  15. html右侧可隐藏导航栏,HTMLCSS----练习隐藏导航栏(三级导航)
  16. 数字电子技术基础——第三章 集成逻辑门电路
  17. python pymysql cursors_怎么Pythonpymysql.cursors从mysql存储过程获取INOUT返回结果
  18. centos安装jq
  19. 动态规划—1.3 九宫格最短路径
  20. unity 物体移动到手机点击位置

热门文章

  1. text无法使用空格 unity_Unity Text添加空格导致换行问题的两种解决方法(还有lua的解决方法)...
  2. 以太坊基金会:Kiln 合并测试网上线公告
  3. 绝地求生:大逃杀(dfs)
  4. MySQL百万数据优化总结 一
  5. 蓝牙耳机什么牌子的好又实惠?便宜又好用的蓝牙耳机推荐
  6. 个人项目总结-瑞吉外卖/传智健康/黑马点评
  7. 【FPGA——基础篇】如何理解时序电路
  8. 文本相似度算法对比分析,判断内容相似的算法有
  9. 如何在Android上恢复已删除的短信
  10. Creator3D:shader11_水面波浪(一)