sync: SHANGHAI-U9 2026-07-27 三个 Simulink→RTL skill 转系统级 + 新增 simulink-rtl-redev
- simulink_rtl_dev / rtl-ip-replace: 从工程级 .claude/skills 迁到用户级并去工程耦合 (模型名/Notes 文档依赖/工程实测基线移出; addpath 与脚本路径改用户级) - rtl-ip-replace/scripts/gen_cmp_tb.py: Vivado 路径不再写死, 改读 VIVADO_PATH / XILINX_VIVADO (兼容 install root / bin / bin\vivado.bat 三种写法), 回落时提示 去设 skills_env.md 的 VIVADO_PATH - simulink-rtl-redev: 新增。原生模型改动后的仿真模型同步规范, 含两个实测过的工具 check_sync.py (三条时序链新鲜度闸门) 与 measure_k.m (时域/频域/包络三估计器互证) - sim-rtl-flow/SKILL.md: 加入 sync_sim:* 步骤, 引用上述三个 skill
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
function rep = audit_hdl_blocks(target, varargin)
|
||||
%AUDIT_HDL_BLOCKS Policy audit of an RTL-bound Simulink subtree (Task12-1).
|
||||
%
|
||||
% Rule 1: everything under a subsystem that is going to be turned into RTL must
|
||||
% be built from Simulink blocks that generate RTL directly -- no embedded
|
||||
% MATLAB (m) code.
|
||||
% Rule 2: an m-code block is only acceptable if a specific Xilinx IP is named
|
||||
% as its replacement AND the swap is proven cycle-true by simulation. Such
|
||||
% blocks must be listed in the exemptions file, with evidence.
|
||||
%
|
||||
% rep = AUDIT_HDL_BLOCKS('model/HdlSubsystem')
|
||||
% rep = AUDIT_HDL_BLOCKS(..., 'Exemptions', 'Notes/hdl_mcode_exemptions.json')
|
||||
% rep = AUDIT_HDL_BLOCKS(..., 'Out', 'hdl_audit.json')
|
||||
%
|
||||
% The report (struct + JSON) lists every offending block with the metrics
|
||||
% needed to plan its migration: script size, persistent state, loops, ports,
|
||||
% and a suggested native-block replacement.
|
||||
%
|
||||
% Verdict: PASS no m-code in the subtree
|
||||
% EXEMPT all m-code blocks are listed as exempted (evidence is
|
||||
% checked separately by check_exemptions.py)
|
||||
% VIOLATION at least one undocumented m-code block
|
||||
|
||||
p = inputParser;
|
||||
p.addParameter('Exemptions', '');
|
||||
p.addParameter('Out', '');
|
||||
p.parse(varargin{:});
|
||||
exFile = p.Results.Exemptions;
|
||||
outFile = p.Results.Out;
|
||||
|
||||
parts = strsplit(target, '/');
|
||||
model = parts{1};
|
||||
if ~bdIsLoaded(model)
|
||||
load_system(model);
|
||||
end
|
||||
|
||||
opts = {'LookUnderMasks', 'all', 'FollowLinks', 'on'};
|
||||
blocks = find_system(target, opts{:}, 'Type', 'Block');
|
||||
|
||||
% ---- classification ------------------------------------------------------
|
||||
% VIOLATION: behaviour written in m code (or opaque to HDL Coder)
|
||||
% WARN : Stateflow control constructs -- allowed for control only, must be
|
||||
% justified; they are not the "embedded m language" this rule targets
|
||||
% but they still hide code from review.
|
||||
VIOL_SF = {'MATLAB Function', 'Truth Table'};
|
||||
WARN_SF = {'Chart', 'State Transition Table'};
|
||||
VIOL_BT = {'MATLABSystem', 'S-Function', 'M-S-Function', 'Fcn', 'MATLABFcn'};
|
||||
|
||||
exempt = local_read_exemptions(exFile);
|
||||
|
||||
items = struct('block', {}, 'kind', {}, 'severity', {}, 'lines', {}, ...
|
||||
'persistent_state', {}, 'loops', {}, 'n_in', {}, 'n_out', {}, ...
|
||||
'suggestion', {}, 'exempt', {}, 'xilinx_ip', {});
|
||||
|
||||
% Stateflow blocks carry an internal ' SFunction ' child; report the owner, not
|
||||
% its implementation detail.
|
||||
sfOwners = {};
|
||||
for k = 1:numel(blocks)
|
||||
if strcmp(get_param(blocks{k}, 'BlockType'), 'SubSystem')
|
||||
try
|
||||
t = get_param(blocks{k}, 'SFBlockType');
|
||||
if ~isempty(t) && ~strcmp(t, 'NONE'), sfOwners{end+1} = blocks{k}; end %#ok<AGROW>
|
||||
catch
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for k = 1:numel(blocks)
|
||||
blk = blocks{k};
|
||||
bt = get_param(blk, 'BlockType');
|
||||
sfType = '';
|
||||
if strcmp(bt, 'SubSystem')
|
||||
try, sfType = get_param(blk, 'SFBlockType'); catch, sfType = ''; end
|
||||
end
|
||||
if isempty(sfType) && any(cellfun(@(o) startsWith(blk, [o '/']), sfOwners))
|
||||
continue % inside a Stateflow block already accounted for
|
||||
end
|
||||
|
||||
if any(strcmp(sfType, VIOL_SF))
|
||||
kind = sfType; sev = 'VIOLATION';
|
||||
elseif any(strcmp(sfType, WARN_SF))
|
||||
kind = sfType; sev = 'WARN';
|
||||
elseif any(strcmp(bt, VIOL_BT))
|
||||
kind = bt; sev = 'VIOLATION';
|
||||
else
|
||||
continue
|
||||
end
|
||||
|
||||
[nl, hasPersist, hasLoop, script] = local_script_metrics(blk, kind);
|
||||
ph = get_param(blk, 'PortHandles');
|
||||
isEx = any(strcmp(blk, {exempt.block}));
|
||||
ip = '';
|
||||
if isEx
|
||||
ip = exempt(strcmp(blk, {exempt.block})).xilinx_ip;
|
||||
sev = 'EXEMPT';
|
||||
end
|
||||
|
||||
items(end+1) = struct( ...
|
||||
'block', blk, 'kind', kind, 'severity', sev, 'lines', nl, ...
|
||||
'persistent_state', hasPersist, 'loops', hasLoop, ...
|
||||
'n_in', numel(ph.Inport), 'n_out', numel(ph.Outport), ...
|
||||
'suggestion', local_suggest(script), 'exempt', isEx, 'xilinx_ip', ip); %#ok<AGROW>
|
||||
end
|
||||
|
||||
nViol = sum(strcmp({items.severity}, 'VIOLATION'));
|
||||
nWarn = sum(strcmp({items.severity}, 'WARN'));
|
||||
nEx = sum(strcmp({items.severity}, 'EXEMPT'));
|
||||
if nViol > 0
|
||||
verdict = 'VIOLATION';
|
||||
elseif nEx > 0
|
||||
verdict = 'EXEMPT';
|
||||
else
|
||||
verdict = 'PASS';
|
||||
end
|
||||
|
||||
rep = struct('target', target, 'model', model, ...
|
||||
'generated_at', datestr(now, 'yyyy-mm-dd HH:MM:SS'), ...
|
||||
'exemptions_file', exFile, 'n_blocks_scanned', numel(blocks), ...
|
||||
'n_violation', nViol, 'n_warn', nWarn, 'n_exempt', nEx, ...
|
||||
'verdict', verdict, 'items', {items});
|
||||
|
||||
% ---- report --------------------------------------------------------------
|
||||
fprintf('\n=== HDL m-code audit: %s ===\n', target);
|
||||
fprintf('scanned %d block(s)\n', numel(blocks));
|
||||
for k = 1:numel(items)
|
||||
it = items(k);
|
||||
fprintf(' [%-9s] %-11s %s\n', it.severity, it.kind, it.block);
|
||||
fprintf(' %d line(s), persistent=%d, loops=%d, %d in / %d out\n', ...
|
||||
it.lines, it.persistent_state, it.loops, it.n_in, it.n_out);
|
||||
if ~isempty(it.xilinx_ip)
|
||||
fprintf(' exempt -> Xilinx IP: %s\n', it.xilinx_ip);
|
||||
elseif ~isempty(it.suggestion)
|
||||
fprintf(' suggest: %s\n', it.suggestion);
|
||||
end
|
||||
end
|
||||
fprintf('VERDICT: %s (violation=%d warn=%d exempt=%d)\n\n', verdict, nViol, nWarn, nEx);
|
||||
|
||||
if ~isempty(outFile)
|
||||
fid = fopen(outFile, 'w');
|
||||
fwrite(fid, jsonencode(rep));
|
||||
fclose(fid);
|
||||
fprintf('report -> %s\n', outFile);
|
||||
end
|
||||
end
|
||||
|
||||
% -------------------------------------------------------------------------
|
||||
function ex = local_read_exemptions(f)
|
||||
ex = struct('block', {}, 'xilinx_ip', {});
|
||||
if isempty(f) || ~isfile(f)
|
||||
return
|
||||
end
|
||||
raw = jsondecode(fileread(f));
|
||||
if isfield(raw, 'exemptions')
|
||||
raw = raw.exemptions;
|
||||
end
|
||||
if isstruct(raw)
|
||||
raw = num2cell(raw);
|
||||
end
|
||||
for i = 1:numel(raw)
|
||||
e = raw{i};
|
||||
ip = '';
|
||||
if isfield(e, 'xilinx_ip'), ip = e.xilinx_ip; end
|
||||
ex(end+1) = struct('block', e.block, 'xilinx_ip', ip); %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
|
||||
function [nl, hasPersist, hasLoop, script] = local_script_metrics(blk, kind)
|
||||
script = '';
|
||||
nl = 0; hasPersist = false; hasLoop = false;
|
||||
if ~strcmp(kind, 'MATLAB Function')
|
||||
return
|
||||
end
|
||||
try
|
||||
c = sfroot().find('-isa', 'Stateflow.EMChart', 'Path', blk);
|
||||
if isempty(c), return, end
|
||||
script = c(1).Script;
|
||||
nl = numel(strsplit(script, newline));
|
||||
hasPersist = ~isempty(regexp(script, '\<persistent\>', 'once'));
|
||||
hasLoop = ~isempty(regexp(script, '^\s*(for|while)\>', 'once', 'lineanchors'));
|
||||
catch
|
||||
end
|
||||
end
|
||||
|
||||
function s = local_suggest(script)
|
||||
%LOCAL_SUGGEST keyword -> native Simulink block hints (see reference/block_substitutions.md)
|
||||
s = '';
|
||||
if isempty(script), return, end
|
||||
hits = {};
|
||||
map = { ...
|
||||
'\<(sin|cos)\s*\(', 'Sine/Cosine: NCO HDL Optimized or Direct Lookup Table (n-D)'; ...
|
||||
'\<atan2?\s*\(', 'atan/atan2: Complex to Magnitude-Angle HDL Optimized (CORDIC)'; ...
|
||||
'\<(sqrt|abs)\s*\(', 'sqrt/abs: Sqrt HDL Optimized / Complex to Magnitude-Angle HDL Optimized'; ...
|
||||
'\<(filter|conv)\s*\(', 'FIR: Discrete FIR Filter (HDL) -- keeps taps as a block parameter'; ...
|
||||
'persistent\s+\w*(buf|mem|ram|dl)', 'sample memory: Simple Dual Port RAM / Delay / Tapped Delay'; ...
|
||||
'\<(fft|ifft)\s*\(', 'FFT: FFT HDL Optimized'; ...
|
||||
'switch\s+\w*(state|st)\>', 'control FSM: Stateflow Chart (control only, no datapath math)'; ...
|
||||
'\<mod\s*\(|\+\s*1\s*;.*count', 'counter: HDL Counter / Counter Free-Running'};
|
||||
for i = 1:size(map, 1)
|
||||
if ~isempty(regexp(script, map{i,1}, 'once'))
|
||||
hits{end+1} = map{i,2}; %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
if isempty(hits)
|
||||
s = 'no keyword match -- rebuild from primitives (Delay/Product/Sum/Switch/Lookup Table)';
|
||||
else
|
||||
s = strjoin(hits, ' | ');
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Gate for rule 2 of Task12-1: an m-code block inside an RTL-bound subtree is
|
||||
acceptable ONLY if a specific Xilinx IP is named as its replacement AND the swap
|
||||
is proven cycle-true by simulation.
|
||||
|
||||
python check_exemptions.py <hdl_audit.json> [<exemptions.json>]
|
||||
|
||||
Exit code 0 = the subtree may go to makehdl / synthesis:
|
||||
every m-code block is either gone, or exempted with (a) a named Xilinx IP and
|
||||
(b) an evidence file whose first line is PASS (rtl-ip-replace cmp_result.txt).
|
||||
Exit code 1 = blocked, with the reason printed per block.
|
||||
|
||||
Evidence paths are resolved relative to the repo root (the directory containing
|
||||
Notes/), or absolutely.
|
||||
"""
|
||||
import json, os, sys
|
||||
|
||||
REQUIRED = ('block', 'reason', 'xilinx_ip', 'cycle_true_evidence', 'approved_by', 'date')
|
||||
|
||||
|
||||
def repo_root(start):
|
||||
d = os.path.abspath(start)
|
||||
while True:
|
||||
if os.path.isdir(os.path.join(d, 'Notes')) or os.path.isdir(os.path.join(d, '.git')):
|
||||
return d
|
||||
nd = os.path.dirname(d)
|
||||
if nd == d:
|
||||
return os.path.abspath(start)
|
||||
d = nd
|
||||
|
||||
|
||||
def evidence_ok(path, roots):
|
||||
tried = []
|
||||
for r in ([''] if os.path.isabs(path) else roots):
|
||||
p = path if os.path.isabs(path) else os.path.join(r, path)
|
||||
tried.append(p)
|
||||
if os.path.isfile(p):
|
||||
first = open(p, errors='replace').readline().strip()
|
||||
if not first.upper().startswith('PASS'):
|
||||
return False, f'evidence is not a PASS: "{first}" ({p})'
|
||||
return True, first
|
||||
return False, 'evidence file not found: ' + ' | '.join(tried)
|
||||
|
||||
|
||||
def main():
|
||||
argv = [a for a in sys.argv[1:] if not a.startswith('--root=')]
|
||||
root_opt = [a.split('=', 1)[1] for a in sys.argv[1:] if a.startswith('--root=')]
|
||||
if not argv:
|
||||
sys.exit(__doc__)
|
||||
audit = json.load(open(argv[0], encoding='utf-8-sig'))
|
||||
exf = argv[1] if len(argv) > 1 else audit.get('exemptions_file') or ''
|
||||
roots = [r for r in (root_opt + [repo_root(os.getcwd()),
|
||||
repo_root(os.path.dirname(os.path.abspath(argv[0])) or '.'),
|
||||
os.getcwd()]) if r]
|
||||
|
||||
items = audit.get('items') or []
|
||||
if isinstance(items, dict):
|
||||
items = [items]
|
||||
mcode = [i for i in items if i['severity'] in ('VIOLATION', 'EXEMPT')]
|
||||
warns = [i for i in items if i['severity'] == 'WARN']
|
||||
|
||||
ex = []
|
||||
if exf and os.path.isfile(exf):
|
||||
raw = json.load(open(exf, encoding='utf-8-sig'))
|
||||
ex = raw.get('exemptions', raw if isinstance(raw, list) else [])
|
||||
by_block = {e.get('block'): e for e in ex}
|
||||
|
||||
print(f"target : {audit.get('target')}")
|
||||
print(f"root : {roots[0]}")
|
||||
print(f"audit : {len(items)} flagged block(s) "
|
||||
f"({len(mcode)} m-code, {len(warns)} stateflow-warn)")
|
||||
print(f"exempt : {exf or '(none)'}\n")
|
||||
|
||||
problems = []
|
||||
for it in mcode:
|
||||
blk = it['block']
|
||||
e = by_block.get(blk)
|
||||
if not e:
|
||||
problems.append(f"{blk}\n m-code with no exemption entry -- rebuild it from "
|
||||
f"native Simulink blocks, or file an exemption (rule 2).")
|
||||
print(f" [BLOCKED ] {blk}")
|
||||
continue
|
||||
missing = [k for k in REQUIRED if not str(e.get(k, '')).strip()]
|
||||
if missing:
|
||||
problems.append(f"{blk}\n exemption is incomplete: missing {', '.join(missing)}")
|
||||
print(f" [BLOCKED ] {blk} (incomplete exemption)")
|
||||
continue
|
||||
ok, info = evidence_ok(e['cycle_true_evidence'], roots)
|
||||
if not ok:
|
||||
problems.append(f"{blk}\n {info}")
|
||||
print(f" [BLOCKED ] {blk} ({info})")
|
||||
continue
|
||||
print(f" [EXEMPT ] {blk}\n IP: {e['xilinx_ip']}\n"
|
||||
f" cycle-true: {info} [{e['cycle_true_evidence']}]")
|
||||
|
||||
for it in warns:
|
||||
print(f" [WARN ] {it['block']} ({it['kind']}) -- control-only Stateflow is tolerated; "
|
||||
f"no datapath arithmetic in it")
|
||||
|
||||
stale = [b for b in by_block if b not in {i['block'] for i in items}]
|
||||
for b in stale:
|
||||
print(f" [STALE ] exemption for a block that is no longer m-code (or gone): {b}")
|
||||
|
||||
if problems:
|
||||
print(f'\nGATE: BLOCKED -- {len(problems)} problem(s)\n')
|
||||
for p in problems:
|
||||
print(' * ' + p)
|
||||
print('\nRule (Task12-1): an RTL-bound subsystem and everything under it must be built '
|
||||
'from Simulink blocks that generate RTL directly; embedded m code is allowed only '
|
||||
'against a named Xilinx IP, proven cycle-true.')
|
||||
sys.exit(1)
|
||||
print('\nGATE: PASS -- no undocumented m code in the RTL-bound subtree')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,64 @@
|
||||
function ok = check_hdl_support(target, varargin)
|
||||
%CHECK_HDL_SUPPORT HDL Coder compatibility check for an RTL-bound subsystem.
|
||||
%
|
||||
% ok = CHECK_HDL_SUPPORT('model/HdlSubsystem')
|
||||
% ok = CHECK_HDL_SUPPORT(..., 'Out', 'hdl_support.txt')
|
||||
%
|
||||
% Wraps checkhdl: catches blocks that cannot become RTL at all -- the other
|
||||
% half of Task12-1 rule 1 ("must be built from blocks that generate RTL
|
||||
% directly"). The m-code policy audit is audit_hdl_blocks.m; run both.
|
||||
|
||||
p = inputParser;
|
||||
p.addParameter('Out', '');
|
||||
p.parse(varargin{:});
|
||||
|
||||
model = strtok(target, '/');
|
||||
if ~bdIsLoaded(model)
|
||||
load_system(model);
|
||||
end
|
||||
|
||||
txt = '';
|
||||
threw = false;
|
||||
try
|
||||
txt = evalc('checkhdl(target)');
|
||||
catch e
|
||||
threw = true;
|
||||
txt = sprintf('checkhdl threw: %s\n%s', e.identifier, e.message);
|
||||
end
|
||||
|
||||
% The summary line is localised ("0 个错误、2 个警告" / "0 errors, 2 warnings"),
|
||||
% so count both spellings rather than grepping for the word "error".
|
||||
nErr = local_count(txt, {'(\d+)\s*个错误', '(\d+)\s+errors?'});
|
||||
nWarn = local_count(txt, {'(\d+)\s*个警告', '(\d+)\s+warnings?'});
|
||||
ok = ~threw && nErr == 0;
|
||||
|
||||
rep = regexp(txt, '(?<=file:///)[^''"<>]*_report\.html', 'match', 'once');
|
||||
fprintf('=== checkhdl %s : %s (%d error(s), %d warning(s)) ===\n', ...
|
||||
target, string(ok), nErr, nWarn);
|
||||
if ~isempty(rep)
|
||||
fprintf('report: %s\n', strrep(rep, '/', filesep));
|
||||
end
|
||||
disp(txt);
|
||||
if nWarn > 0
|
||||
fprintf(['NOTE: warnings are not a pass/fail gate here, but read them -- they are usually\n' ...
|
||||
' "this block will cost you area/latency in RTL" hints.\n']);
|
||||
end
|
||||
|
||||
if ~isempty(p.Results.Out)
|
||||
fid = fopen(p.Results.Out, 'w');
|
||||
fwrite(fid, txt);
|
||||
fclose(fid);
|
||||
fprintf('checkhdl log -> %s\n', p.Results.Out);
|
||||
end
|
||||
end
|
||||
|
||||
function n = local_count(txt, pats)
|
||||
n = 0;
|
||||
for i = 1:numel(pats)
|
||||
t = regexp(txt, pats{i}, 'tokens', 'once');
|
||||
if ~isempty(t)
|
||||
n = str2double(t{1});
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"_README": "Rule 2 of Task12-1: m code inside an RTL-bound subtree is only acceptable against a NAMED Xilinx IP, proven cycle-true. Copy this file to Notes/<model>_mcode_exemptions.json and fill it in. Every field is mandatory; check_exemptions.py rejects the entry otherwise.",
|
||||
"model": "dpd_ntn_05",
|
||||
"hdl_target": "dpd_ntn_05/DpdAlign_HDL",
|
||||
"exemptions": [
|
||||
{
|
||||
"block": "dpd_ntn_05/DpdAlign_HDL/RxAlign_HDL/RxAlignCore",
|
||||
"reason": "为什么原生 Simulink 模块搭不出来(必须具体:算法结构/时序/资源,不接受“太麻烦”)",
|
||||
"xilinx_ip": "cordic 6.0 (Arc_Tan) + dist_mem_gen sin/cos ROM + blk_mem_gen SDP",
|
||||
"ip_scope": "IP 覆盖的范围;剩下仍由 m 代码实现的部分要写清楚,它们仍然是欠账",
|
||||
"cycle_true_evidence": "vivado/rtl_ip/dpd_ntn_05/cmp/cmp_result.txt",
|
||||
"evidence_kind": "rtl-ip-replace 双 DUT 逐拍对比(首行须为 PASS)",
|
||||
"approved_by": "bowei",
|
||||
"date": "2026-07-24"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
function hits = hdl_block_search(varargin)
|
||||
%HDL_BLOCK_SEARCH Find a native, RTL-generatable Simulink block (Task12-1 rule 1).
|
||||
%
|
||||
% hdl_block_search list every block in the HDL Coder library
|
||||
% hdl_block_search('ram') blocks whose name matches a keyword
|
||||
% hdl_block_search('sin','cos') several keywords (OR)
|
||||
%
|
||||
% Source of truth is the HDL Coder block library (hdlsllib) plus the HDL
|
||||
% Optimized blocks of DSP HDL / Wireless HDL / Vision HDL: anything in there
|
||||
% generates RTL directly, so it is a legal building block. Anything NOT in
|
||||
% there needs checkhdl before you rely on it.
|
||||
|
||||
% Sub-libraries are listed individually: the top-level DSP HDL library holds
|
||||
% links, which find_system will not walk into.
|
||||
libs = {'hdlsllib', ... % HDL Coder baseline set
|
||||
'dsphdlfiltering2', 'dsphdlmathfun2', ... % FIR/CIC/LMS/Biquad, CORDIC
|
||||
'dsphdlxfrm2', 'dsphdlsigops2', 'dsphdlsrcs2', ...% FFT/IFFT, Farrow/NCO/resample
|
||||
'whdllib', 'visionhdllib'};
|
||||
hits = {};
|
||||
for i = 1:numel(libs)
|
||||
try
|
||||
load_system(libs{i});
|
||||
catch
|
||||
continue
|
||||
end
|
||||
blks = find_system(libs{i}, 'SearchDepth', 5, 'Type', 'Block');
|
||||
for k = 1:numel(blks)
|
||||
nm = strrep(get_param(blks{k}, 'Name'), newline, ' ');
|
||||
if isempty(varargin) || any(cellfun(@(p) ~isempty(regexpi(nm, p, 'once')), varargin))
|
||||
hits{end+1, 1} = nm; %#ok<AGROW>
|
||||
hits{end, 2} = blks{k}; %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if isempty(hits)
|
||||
fprintf(['no HDL library block matches %s\n' ...
|
||||
'-> if you cannot build it from these blocks, rule 2 applies: name the Xilinx IP\n' ...
|
||||
' that will implement it, prove the swap cycle-true, and file the exemption.\n'], ...
|
||||
strjoin(varargin, ', '));
|
||||
return
|
||||
end
|
||||
|
||||
[~, ia] = unique(hits(:, 1));
|
||||
hits = hits(sort(ia), :);
|
||||
fprintf('%d HDL-generatable block(s):\n', size(hits, 1));
|
||||
for k = 1:size(hits, 1)
|
||||
fprintf(' %-42s %s\n', hits{k, 1}, hits{k, 2});
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user