Files
boweiandClaude Opus 5 e07cfaa6d1 sync: WIN-BOWEI 2026-08-10 fpga_flash 增 Zynq BOOT.bin/QSPI 脚本,rtl-ip-replace 与 sim-rtl-flow 更新
- 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>
2026-08-19 12:50:44 +08:00

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"