- fpga_flash: 新增 build_zynq_boot.py / program_zynq_qspi.py(bootgen + program_flash, 含备份、SHA-256/容量/marker 守卫);vivado_env.py 增 resolve_sdk_dir(SDK_ROOT/SDK_PATH); SKILL.md 补 Zynq 烧写流程,示例去掉写死的 D:\Xilinx\SDK\2018.1,改由 skills_env.md 解析。 - rtl-ip-replace: gen_ip.tcl / namespace_ip.py 更新。 - sim-rtl-flow: SKILL.md 与 run_sim_rtl_flow.py 更新。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
213 lines
12 KiB
Tcl
213 lines
12 KiB
Tcl
# =============================================================================
|
|
# gen_ip.tcl -- create the Vivado IPs listed in rtl_ip/ip_manifest.tcl
|
|
# usage: vivado -mode batch -source gen_ip.tcl -tclargs <ip_manifest.tcl> <ip_out_dir> [part]
|
|
# Managed-IP style: .xci + synthesis + simulation targets are generated so the
|
|
# cores can be (a) added to the synthesis project and (b) compiled for xsim.
|
|
# =============================================================================
|
|
set manifest [lindex $argv 0]
|
|
set ipdir [file normalize [lindex $argv 1]]
|
|
set part xc7z100ffg900-2
|
|
if {[llength $argv] > 2} { set part [lindex $argv 2] }
|
|
|
|
file mkdir $ipdir
|
|
create_project -in_memory -part $part
|
|
set_property target_language Verilog [current_project]
|
|
|
|
set ips {}
|
|
source $manifest ;# fills $ips: list of {type {..} name {..} width {..} depth {..} coe {..} mode {..}}
|
|
|
|
# manifest field with a default (older manifests do not carry the newer keys)
|
|
proc opt {arrname key def} {
|
|
upvar 1 $arrname c
|
|
if {[info exists c($key)] && $c($key) ne {}} { return $c($key) }
|
|
return $def
|
|
}
|
|
|
|
foreach ip $ips {
|
|
array unset c; array set c $ip
|
|
set name $c(name)
|
|
if {[file exists $ipdir/$name/$name.xci]} {
|
|
puts "###### SKIP $name (exists)"
|
|
continue
|
|
}
|
|
switch -- $c(type) {
|
|
dist_mem_gen {
|
|
# mode {rom} (default) -- constant lookup table, asynchronous read.
|
|
# mode {dual_port_ram} -- LUTRAM, port A = write + async read (spo),
|
|
# port B = async read (dpo), NO output registers. Use this (not
|
|
# blk_mem_gen) whenever the generated RAM needs a DEFINED read of
|
|
# the pre-write word while the other port writes the same address:
|
|
# a block-RAM primitive leaves the opposite port's read data
|
|
# undefined on such a collision (PG058), distributed RAM does not.
|
|
# The output registers / write-first read-back stay in the RTL
|
|
# wrapper so latency and enb gating remain cycle-identical.
|
|
set dmode [opt c mode {rom}]
|
|
create_ip -name dist_mem_gen -vendor xilinx.com -library ip -version 8.0 \
|
|
-module_name $name -dir $ipdir
|
|
if {$dmode eq "dual_port_ram"} {
|
|
set_property -dict [list \
|
|
CONFIG.memory_type {dual_port_ram} \
|
|
CONFIG.data_width $c(width) \
|
|
CONFIG.depth $c(depth) \
|
|
CONFIG.input_options {non_registered} \
|
|
CONFIG.output_options {non_registered} \
|
|
CONFIG.dual_port_address {non_registered} \
|
|
CONFIG.input_clock_enable {false} \
|
|
CONFIG.default_data {0} \
|
|
] [get_ips $name]
|
|
} else {
|
|
set_property -dict [list \
|
|
CONFIG.memory_type {rom} \
|
|
CONFIG.data_width $c(width) \
|
|
CONFIG.depth $c(depth) \
|
|
CONFIG.coefficient_file $c(coe) \
|
|
] [get_ips $name]
|
|
}
|
|
}
|
|
blk_mem_gen {
|
|
# mode {sdp_readfirst_commonclk} (default): SDP, common clock,
|
|
# READ_FIRST on the write port, no output register, ENA/ENB pins --
|
|
# matches SimpleDualPortRAM_generic semantics exactly.
|
|
# mode {tdp_writefirst_commonclk}: TRUE dual port, port A
|
|
# WRITE_FIRST (write with read-back), port B READ_FIRST.
|
|
# CAUTION: a BRAM primitive does NOT define the port-B read data
|
|
# when port A writes the same address in the same cycle; only use
|
|
# this when the design provably never collides.
|
|
set bmode [opt c mode {sdp_readfirst_commonclk}]
|
|
set mtype {Simple_Dual_Port_RAM}
|
|
set opa {READ_FIRST}
|
|
if {$bmode eq "tdp_writefirst_commonclk"} {
|
|
set mtype {True_Dual_Port_RAM}
|
|
set opa {WRITE_FIRST}
|
|
}
|
|
create_ip -name blk_mem_gen -vendor xilinx.com -library ip -version 8.4 \
|
|
-module_name $name -dir $ipdir
|
|
set_property -dict [list \
|
|
CONFIG.Memory_Type $mtype \
|
|
CONFIG.Assume_Synchronous_Clk {true} \
|
|
CONFIG.Use_Byte_Write_Enable {false} \
|
|
CONFIG.Write_Width_A $c(width) \
|
|
CONFIG.Write_Depth_A $c(depth) \
|
|
CONFIG.Read_Width_A $c(width) \
|
|
CONFIG.Write_Width_B $c(width) \
|
|
CONFIG.Read_Width_B $c(width) \
|
|
CONFIG.Operating_Mode_A $opa \
|
|
CONFIG.Enable_A {Use_ENA_Pin} \
|
|
CONFIG.Enable_B {Use_ENB_Pin} \
|
|
CONFIG.Register_PortB_Output_of_Memory_Primitives {false} \
|
|
CONFIG.Register_PortB_Output_of_Memory_Core {false} \
|
|
] [get_ips $name]
|
|
if {$bmode eq "tdp_writefirst_commonclk"} {
|
|
set_property -dict [list \
|
|
CONFIG.Operating_Mode_B {READ_FIRST} \
|
|
CONFIG.Register_PortA_Output_of_Memory_Primitives {false} \
|
|
CONFIG.Register_PortA_Output_of_Memory_Core {false} \
|
|
] [get_ips $name]
|
|
}
|
|
}
|
|
fifo_generator {
|
|
# guided: mode field carries standard|fwft; depth/width from manifest
|
|
create_ip -name fifo_generator -vendor xilinx.com -library ip -version 13.2 \
|
|
-module_name $name -dir $ipdir
|
|
set rm {Standard_FIFO}
|
|
if {[info exists c(mode)] && $c(mode) eq "fwft"} { set rm {First_Word_Fall_Through} }
|
|
set_property -dict [list \
|
|
CONFIG.Fifo_Implementation {Common_Clock_Block_RAM} \
|
|
CONFIG.Performance_Options $rm \
|
|
CONFIG.Input_Data_Width $c(width) \
|
|
CONFIG.Input_Depth $c(depth) \
|
|
CONFIG.Output_Data_Width $c(width) \
|
|
CONFIG.Output_Depth $c(depth) \
|
|
] [get_ips $name]
|
|
}
|
|
cordic {
|
|
# guided: Arc_Tan phase-seed kernel. Coarse rotation = full-quadrant atan2
|
|
# (bidirectional-adjustment principle: better than the FSM's RHP quirk);
|
|
# Radians 3QN phase; Truncate rounding; ACLKEN so enb gating stays exact;
|
|
# parallel/maximum pipelining -> latency ~input_width+4, well under the
|
|
# 64-cycle commit budget.
|
|
create_ip -name cordic -vendor xilinx.com -library ip -version 6.0 \
|
|
-module_name $name -dir $ipdir
|
|
set_property -dict [list \
|
|
CONFIG.Functional_Selection {Arc_Tan} \
|
|
CONFIG.Input_Width $c(width) \
|
|
CONFIG.Output_Width $c(out_width) \
|
|
CONFIG.Data_Format {SignedFraction} \
|
|
CONFIG.Phase_Format {Radians} \
|
|
CONFIG.Coarse_Rotation {true} \
|
|
CONFIG.Round_Mode {Truncate} \
|
|
CONFIG.ACLKEN {true} \
|
|
CONFIG.Pipelining_Mode {Maximum} \
|
|
] [get_ips $name]
|
|
}
|
|
fir_compiler {
|
|
# guided (Task12 §六). Cycle-true-critical settings:
|
|
# Integer_Coefficients : taps go in as integers, the IP must NOT re-quantise
|
|
# Full_Precision output : truncation stays OUTSIDE, done the way the RTL did it
|
|
# Input_Sample_Period/1 : one sample per clock, no hardware folding
|
|
# -> the latency is a fixed number we can align to
|
|
# ACLKEN : gated by enb exactly like the generated RTL
|
|
# no TREADY / no input FIFO: pure pipeline, no variable-latency handshake
|
|
create_ip -name fir_compiler -vendor xilinx.com -library ip -version 7.2 \
|
|
-module_name $name -dir $ipdir
|
|
set_property -dict [list \
|
|
CONFIG.Filter_Type [opt c filter_type {Single_Rate}] \
|
|
CONFIG.CoefficientSource {COE_File} \
|
|
CONFIG.Coefficient_File $c(coe) \
|
|
CONFIG.Quantization {Integer_Coefficients} \
|
|
CONFIG.Coefficient_Width $c(coef_width) \
|
|
CONFIG.Coefficient_Fractional_Bits [opt c coef_frac 0] \
|
|
CONFIG.Coefficient_Sign {Signed} \
|
|
CONFIG.Coefficient_Structure [opt c coef_structure {Inferred}] \
|
|
CONFIG.Data_Width $c(width) \
|
|
CONFIG.Data_Fractional_Bits [opt c frac 0] \
|
|
CONFIG.Data_Sign {Signed} \
|
|
CONFIG.Output_Rounding_Mode {Full_Precision} \
|
|
CONFIG.RateSpecification {Input_Sample_Period} \
|
|
CONFIG.SamplePeriod 1 \
|
|
CONFIG.HardwareOversamplingRate 1 \
|
|
CONFIG.Interpolation_Rate [opt c interp 1] \
|
|
CONFIG.Decimation_Rate [opt c decim 1] \
|
|
CONFIG.Zero_Pack_Factor [opt c zero_pack 1] \
|
|
CONFIG.Number_Channels [opt c channels 1] \
|
|
CONFIG.Number_Paths [opt c paths 1] \
|
|
CONFIG.Coefficient_Reload [opt c reload {false}] \
|
|
CONFIG.Num_Reload_Slots [opt c reload_slots 1] \
|
|
CONFIG.Has_ACLKEN [opt c aclken {true}] \
|
|
CONFIG.Has_ARESETn [opt c aresetn {false}] \
|
|
CONFIG.M_DATA_Has_TREADY {false} \
|
|
CONFIG.S_DATA_Has_FIFO {false} \
|
|
] [get_ips $name]
|
|
}
|
|
cic_compiler {
|
|
# guided: only for large integer rate changes (CIC), fixed rate so the
|
|
# decimation/interpolation cadence is static and comparable cycle by cycle.
|
|
create_ip -name cic_compiler -vendor xilinx.com -library ip -version 4.0 \
|
|
-module_name $name -dir $ipdir
|
|
set rate [opt c rate 1]
|
|
set_property -dict [list \
|
|
CONFIG.Filter_Type [opt c filter_type {Decimation}] \
|
|
CONFIG.Number_Of_Stages [opt c stages 3] \
|
|
CONFIG.Differential_Delay [opt c diff_delay 1] \
|
|
CONFIG.Sample_Rate_Changes {Fixed} \
|
|
CONFIG.Fixed_Or_Initial_Rate $rate \
|
|
CONFIG.Minimum_Rate [opt c min_rate $rate] \
|
|
CONFIG.Maximum_Rate [opt c max_rate $rate] \
|
|
CONFIG.Input_Data_Width $c(width) \
|
|
CONFIG.Quantization {Full_Precision} \
|
|
CONFIG.RateSpecification {Sample_Period} \
|
|
CONFIG.SamplePeriod 1 \
|
|
CONFIG.Number_Of_Channels [opt c channels 1] \
|
|
CONFIG.Use_Xtreme_DSP_Slice {false} \
|
|
CONFIG.HAS_ACLKEN [opt c aclken {true}] \
|
|
CONFIG.HAS_ARESETN [opt c aresetn {false}] \
|
|
CONFIG.HAS_DOUT_TREADY {false} \
|
|
] [get_ips $name]
|
|
}
|
|
default { puts "###### UNKNOWN IP TYPE: $c(type) ($name)"; continue }
|
|
}
|
|
generate_target {instantiation_template synthesis simulation} [get_ips $name]
|
|
puts "###### GENERATED $name ($c(type))"
|
|
}
|
|
puts "###### DONE [llength $ips] ip(s) in $ipdir"
|