FPGA 令牌桶限速:补充与消费同拍时,信用先饱和还是先扣减

【信息科学与工程学】计算机科学与自动化——第三百零五篇 数据中心 Scale-Up、Scale-Out、Scale-Across 100 密码法》《商用密码管理条例》《网络安全法》《数据安全法》《国家政务信息化项目建设管理办法》;《网络安全法》《数据安全法》《个人信息保护法》《反恐怖主义法》《反有组织犯罪法》;反洗钱可疑交易规则判定。《个人信息保护法》《网络安全法》《数据安全法》《反不正当竞争法》《生成式人工智能服务管理暂行办法》;《中国共产党纪律处分条例》《党内监督条例》《巡视工作条例》《审计法》《公职人员政务处分法》《反腐败/反有组织犯罪相关》《档案法》《电子签名法》《会计法》(原始凭证数字化要求)、《网络安全法》; 阅读详情

一条数据通路平时流量不大,偶尔会连续送来一批请求。如果下游只能长期承担固定工作量,又允许短时间突发,单纯“每隔 N 拍放行一次”就不够灵活。令牌桶把空闲时间积累成有限信用:有信用就能发,没信用就对上游背压。

本文实现一个单时钟、可变成本的 ready/valid 限速器。每项请求携带 cost,一次 tick 补充 REFILL 个信用,信用上限为 CAPACITY。重点是同拍补充与消费、算术扩位、非法成本和背压期间的稳定性。

实际验证环境:Windows 上的 Vivado/XSim 2022.1,SW Build 3526262,2026-11-06。 本文的 6 组正向配置共完成 89,115 个自检周期,6 个指定 RTL 变异和 8 个非法参数均被捕获;另有独立测试台完成 175,621 次断言。验证限于同一时钟域的 RTL 二态行为,没有进行综合、布局布线、时序闭合或上板测试。下文将契约推导表与真实 DUT 记录分开标注。

1. 先给“成本”确定单位

成本可以是一个请求占用的工作量、待处理字节数,也可以统一为 1。模块只接收一个无符号整数,不会解析数据内容或替调用方计算包长。

本文约定:

  • CAPACITY 为 1~65535,REFILL 为 1~CAPACITY。
  • INITIAL_TOKENS 为 0~CAPACITY,表示复位后是否允许立即突发,默认 0。
  • 合法成本为 1~CAPACITY。0 成本被拒绝,避免流量绕过预算;超过容量的项永远不可能被服务。
  • DATA_W 为 1~64,成本位宽 CW 自动由容量计算。
  • tick 是与 clk 同域的补充使能,不是另一个时钟。连续为 1 就是每拍补充。
  • rst 同步高有效,在上升沿把信用恢复为 INITIAL_TOKENS;但 rst=1 时会立即组合屏蔽输入、输出握手。使用前至少经过一次有效复位沿,所有输入来自同一时钟域并满足建立、保持要求。

例如信用单位为字节,REFILL=3,补充脉冲平均每 10 个时钟出现一次,时钟频率为 f_clk,长期补充预算为 3*f_clk/10 字节每秒。这个算式没有包含真实链路开销,也没有证明电路能达到某个时钟频率。

RFC 2697 第3节给出了有上限的令牌计数及按包大小扣减的例子。该文档实际定义的是双桶三色标记器;本文是单桶、离散 tick、通过背压整形的教学模块,不宣称实现 RFC 2697 的完整行为。

2. 用一条递推式固定同拍顺序

设时钟边沿前的信用为 T,本拍是否补充为 tick,请求成本为 c。先计算:

raw       = T + (tick ? REFILL : 0)
available = min(CAPACITY, raw)
fire      = m_valid && m_ready
T_next    = available - (fire ? c : 0)

这份契约明确采用“先补充并饱和,再消费”。已经溢出桶的信用不能因为本拍恰好消费而重新找回来。

以 CAPACITY=7、REFILL=3 为例,下表每行都表示一个即将发生的上升沿:

Ttickvalidcostreadyavailable本拍消费T_next
00121000
01121321
11140404
40140404
41141743
31011606
61111716

最后一行最容易暴露顺序错误。正确结果是 min(7,6+3)-1=6;若写成 min(7,6+3-1),结果会是 7。这两个实现对应不同的边界规则,测试必须先选定其中一种,而不是看到结果不同才调整期望。

每拍溢出的信用为 lost=max(0,raw-CAPACITY)。在一个没有复位的区间中,应满足:

末态信用 = 初态信用 + 补充总量 - 饱和丢弃总量 - 已消费成本总量

因此,整个区间实际消费不会超过初始信用与区间补充量之和。这里约束的是成本总量,不是请求数量;成本不同,同样的令牌预算能服务的请求数量也不同。

真实 DUT 记录:背压期间不扣费,解除背压后再消费

下图来自本次 XSim 原生仿真的 token_positive_2/trace.csv,参数为 CAPACITY=7、REFILL=3、INITIAL_TOKENS=0、DATA_W=16。使用连续 cycle 2~13 的全部12行,只绘制实际 dut_* 观测和施加的输入,没有把 exp_* 参考值当作实测波形。

XSim 2022.1 实际 DUT 信用与握手记录

cycle 2 的信用从3补到6,cycle 3补充后饱和到7;cycle 6~12中,载荷 0x55aa 和valid保持,ready为0,信用保持7。cycle 13下游ready拉高,先把补充后的信用限制为7,再扣除成本1,实际边沿后信用为6。dut_tokens_before 采于边沿前,dut_tokens_after 采于上升沿后1 ns的测试台检查点;这个1 ns不是被测模块的物理延迟。

3. 为什么加法必须比信用寄存器宽一位

容量为 7 时,信用需要 3 位,但 7+3=10 需要 4 位。如果先在 3 位里相加,再与容量比较,结果已经回绕成 2,后面的饱和判断无法恢复原值。

代码把信用与补充量都扩到 CW+1 位后相加。由于 T<=CAPACITY 且 REFILL<=CAPACITY,宽一位足以表示本例的最大加数和。只有比较并截取合法结果之后,才写回 CW 位寄存器。

非法参数的仿真检查放在 initial 中,用 PARAM_ERROR 明确终止。CW 和数据端口宽度对非法值先采用安全的占位宽度,目的是让测试尽可能走到明确诊断,而不是先因零宽度或异常范围无法展开。占位宽度不表示非法参数受支持;综合前仍必须由工程配置检查参数范围。

4. 一次握手只扣一次信用

这里没有数据缓存,载荷和成本组合直通。上游必须遵守 ready/valid 约定:一旦提出有效请求,如果 s_ready=0,就保持 valid、data、cost,直到握手或复位。

本模块的控制关系为:

can_send = 成本合法 && 成本不大于本拍 available
m_valid  = !rst && s_valid && can_send
s_ready  = !rst && can_send && m_ready

m_valid 不等待 m_ready。只有两者同为 1,才真正扣减信用。输入与输出握手严格同时发生,因此不会出现已经接受输入、却没有地方保存载荷的问题。基本握手定义可参考 AMD PG256 的 AXI4-Stream Interface。本文使用相同的握手思想,但不是完整 AXI4-Stream IP。

当 m_valid=1、m_ready=0 时,信用不会被消费。上一拍可用信用会写回寄存器,下一拍只可能保持或增加,不能因补充脉冲消失而不足。因此,只要上游保持请求,已经拉高的 m_valid 就保持有效,数据与成本也保持不变。复位是这条持续性约定的明确例外。

5. 非法成本不能悄悄免费通行

当 s_valid=1 且成本为 0 或超过容量时,cost_error=1,同时关闭输入 ready 和输出 valid。信用计数仍可补充。

这是一种阻塞式错误接口:它既不会自动丢弃输入,也不会把它当成零成本请求接受。如果系统允许不可信数据到达这里,需要在上游增加显式的错误处理或丢弃状态机。不要一边要求上游无条件保持 valid,一边又期望非法请求自动消失。

另一个限制是队头阻塞。如果当前项需要 7 个信用,后面的一项只需 1 个信用,本模块不能绕过前者。需要重排序、优先级或分流时,应在更高层设计调度器。

6. 完整 RTL

保存为 token_bucket.sv。下面代码由同名源文件直接嵌入,避免文章与交付源码两处手工维护。

本次原生结果绑定实际受测源码哈希;下面发布副本仅更新第2行过时注释,其他字节经独立核对一致。没有把新注释对应的文件哈希宣称为重新跑过XSim。

// Single-clock ready/valid limiter. Refill is saturated BEFORE spending.
// Behavioral example; synthesis and hardware validation not performed.
module token_bucket #(
    parameter integer CAPACITY=7,
    parameter integer REFILL=3,
    parameter integer INITIAL_TOKENS=0,
    parameter integer DATA_W=16,
    localparam integer CW=(CAPACITY<1 || CAPACITY>65535) ? 1 : $clog2(CAPACITY+1),
    localparam integer DW=(DATA_W<1 || DATA_W>64) ? 1 : DATA_W
) (
    input logic clk, rst, tick,
    input logic s_valid,
    output logic s_ready,
    input logic [DW-1:0] s_data,
    input logic [CW-1:0] s_cost,
    output logic m_valid,
    input logic m_ready,
    output logic [DW-1:0] m_data,
    output logic [CW-1:0] m_cost,
    output logic cost_error,
    output logic [CW-1:0] tokens
);
    // The wider sum represents up to 2*CAPACITY without wraparound.
    logic [CW:0] raw_credit;
    logic [CW-1:0] available;
    logic legal_cost, can_send;
    assign raw_credit={1'b0,tokens} + (tick ? (CW+1)'(REFILL) : (CW+1)'(0));
    assign available=(raw_credit > (CW+1)'(CAPACITY)) ? CW'(CAPACITY) : raw_credit[CW-1:0];
    assign legal_cost=(s_cost != '0) && ({1'b0,s_cost} <= (CW+1)'(CAPACITY));
    assign can_send=legal_cost && (s_cost <= available);
    assign s_ready=!rst && can_send && m_ready;
    assign m_valid=!rst && s_valid && can_send;
    assign m_data=s_data;
    assign m_cost=s_cost;
    assign cost_error=!rst && s_valid && !legal_cost;
    always_ff @(posedge clk) begin
        if(rst) tokens <= CW'(INITIAL_TOKENS);
        else if(m_valid && m_ready) tokens <= available-s_cost;
        else tokens <= available;
    end
    // synthesis translate_off
    initial begin
        if(CAPACITY<1 || CAPACITY>65535 || REFILL<1 || REFILL>CAPACITY ||
           INITIAL_TOKENS<0 || INITIAL_TOKENS>CAPACITY || DATA_W<1 || DATA_W>64)
            $fatal(1,"PARAM_ERROR token_bucket");
    end
    // synthesis translate_on
endmodule

7. 自检为什么既看数值,也看协议

完整测试台在下一节,检查包括:

  1. 复位初值、容量为 1、非二次幂容量、最大合法容量以及 1/8/16/32/64 位载荷。
  2. 补充与消费同拍、桶满后继续补充、从空桶使用本拍补充、成本恰好等于容量。
  3. 下游连续不就绪时不扣信用,输出 valid、data、cost 稳定。
  4. 零成本和可表示的超容量成本错误注入。
  5. 复位取消被背压的项,随后从新的信用初值开始。
  6. 小容量配置下,重建每一种信用状态,遍历 tick、valid、ready 和全部可表示成本。
  7. 使用固定种子的随机流量,未接受的上游项保持不变;结束时恢复补充和 ready,检查能排空。

测试台同时累计“补充、饱和丢弃、实际消费”,核对信用守恒。输出 CSV 的 exp_* 与 dut_* 分别保存预期关系和实际观测,包含边沿前信用/控制与边沿后信用,可用于定位第一处错误,而不只是阅读最后一行 PASS。

Python 参考检查另用“令牌列表增加、裁剪、删除”的模型对照整数递推关系,已经检查 310,624 个状态/输入组合,并发现 5 类参考模型错误的反例。这只是参考关系检查,不编译、不模拟本篇 RTL。

本次原生批跑执行了 6 个 RTL 变异:去掉饱和而直接截断回绕、用旧信用判断、valid 即扣款、允许零成本、扣减后再饱和、valid 依赖 ready。六个变异均先成功编译展开,再由事先指定的断言判失败。编译错误、进程超时或不相关断言没有计作“成功杀死”。本次 XSim 在 $fatal 后仍可能返回进程码0,因此运行器同时检查指定诊断、进程完成状态和相反的PASS标记,不能只按退出码判断。

8. 完整自检测试台

保存为 tb_token_bucket.sv。仿真激励在下降沿驱动,在下一上升沿之后检查寄存器结果;这是避免测试台与被测时序逻辑在同一调度区域争用的安排,不是物理建立保持验证。

`timescale 1ns/1ps
module tb_token_bucket #(
    parameter integer CAPACITY=7, REFILL=3, INITIAL_TOKENS=0, DATA_W=16,
    localparam integer CW=(CAPACITY<1 || CAPACITY>65535) ? 1 : $clog2(CAPACITY+1)
);
    logic clk=0;
    always #5 clk=~clk;
    logic rst=1, tick=0, s_valid=0, s_ready, m_valid, m_ready=0, cost_error;
    logic [DATA_W-1:0] s_data='0, m_data;
    logic [CW-1:0] s_cost='0, m_cost, tokens;
    token_bucket #(.CAPACITY(CAPACITY), .REFILL(REFILL),
        .INITIAL_TOKENS(INITIAL_TOKENS), .DATA_W(DATA_W)) dut(.*);
    integer model=INITIAL_TOKENS, checks=0, transfers=0;
    integer trace_fd;
    integer total_add=0, total_spend=0, total_lost=0, epoch_initial=INITIAL_TOKENS;
    bit was_stalled=0;
    logic [DATA_W-1:0] stalled_data;
    logic [CW-1:0] stalled_cost;
    bit last_accept=0;
    integer unsigned rng=32'h537a9c01;
    function automatic integer unsigned next_random();
        rng=rng^(rng<<13); rng=rng^(rng>>17); rng=rng^(rng<<5);
        return rng;
    endfunction
    function automatic logic [DATA_W-1:0] next_payload();
        logic [63:0] word;
        word[31:0]=next_random(); word[63:32]=next_random();
        return DATA_W'(word);
    endfunction
    task automatic step(input bit r, t, v, input integer c,
                         input bit ready, input logic [DATA_W-1:0] d);
        integer available, raw, expected_next;
        bit legal, ev, er, ee;
        logic [CW-1:0] dut_tokens_before,dut_cost_before;
        logic [DATA_W-1:0] dut_data_before;
        logic dut_valid_before,dut_ready_before,dut_error_before;
        @(negedge clk);
        rst=r; tick=t; s_valid=v; s_cost=CW'(c); m_ready=ready; s_data=d;
        #1;
        // Snapshot the DUT before the clock edge, separate from expectations.
        dut_tokens_before=tokens; dut_cost_before=m_cost; dut_data_before=m_data;
        dut_valid_before=m_valid; dut_ready_before=s_ready; dut_error_before=cost_error;
        raw=model+(t ? REFILL : 0);
        available=(raw>CAPACITY) ? CAPACITY : raw;
        legal=(c>=1 && c<=CAPACITY);
        ev=!r && v && legal && (c<=available);
        er=!r && legal && (c<=available) && ready;
        ee=!r && v && !legal;
        if (m_valid !== ev || s_ready !== er || cost_error !== ee)
            $fatal(1,"ASSERT_TOKEN_CONTROL checks=%0d model=%0d cost=%0d",checks,model,c);
        if (m_data !== d || m_cost !== CW'(c)) $fatal(1,"ASSERT_TOKEN_PAYLOAD");
        if (was_stalled && !r && (!m_valid || m_data!==stalled_data || m_cost!==stalled_cost))
            $fatal(1,"ASSERT_TOKEN_STABILITY");
        was_stalled=ev && !ready;
        stalled_data=d; stalled_cost=CW'(c);
        last_accept=v && er;
        if (last_accept != (ev && ready)) $fatal(1,"ASSERT_TOKEN_HANDSHAKE");
        if (r) begin
            expected_next=INITIAL_TOKENS;
            total_add=0; total_spend=0; total_lost=0; epoch_initial=INITIAL_TOKENS;
        end else begin
            expected_next=available-(last_accept ? c : 0);
            total_add=total_add+(t ? REFILL : 0);
            total_lost=total_lost+((raw>CAPACITY) ? raw-CAPACITY : 0);
            total_spend=total_spend+(last_accept ? c : 0);
        end
        @(posedge clk); #1;
        // Record both actual ports/state and independent model values.
        $fdisplay(trace_fd,"%0d,%0d,%0d,%0d,%0d,%0d,%0h,%0d,%0d,%0d,%0d,%0d,%0d,%0d,%0d,%0d,%0h,%0d,%0d,%0d,%0d,%0d",
            checks,r,t,v,c,ready,d,model,dut_tokens_before,available,
            ev,dut_valid_before,er,dut_ready_before,ee,dut_error_before,
            dut_data_before,dut_cost_before,last_accept,(dut_valid_before && ready),expected_next,tokens);
        model=expected_next;
        if (tokens !== CW'(model)) $fatal(1,"ASSERT_TOKEN_STATE checks=%0d got=%0d expected=%0d",checks,tokens,model);
        if (model<0 || model>CAPACITY || model!=epoch_initial+total_add-total_lost-total_spend)
            $fatal(1,"ASSERT_TOKEN_CONSERVATION");
        if (last_accept) transfers=transfers+1;
        checks=checks+1;
    endtask
    task automatic reset_case();
        step(1,0,0,1,0,'0);
    endtask
    task automatic fill();
        for(integer i=0;i<(CAPACITY+REFILL-1)/REFILL+2;i=i+1)
            step(0,1,0,1,0,'0);
    endtask
    bit rv, rt, rr;
    integer rc;
    logic [DATA_W-1:0] rd;
    initial begin
        trace_fd=$fopen("trace.csv","w");
        if(!trace_fd) $fatal(1,"TRACE_OPEN_ERROR");
        $fdisplay(trace_fd,"cycle,rst,tick,s_valid,s_cost,m_ready,s_data_hex,exp_tokens_before,dut_tokens_before,exp_available,exp_m_valid,dut_m_valid,exp_s_ready,dut_s_ready,exp_cost_error,dut_cost_error,dut_m_data_hex,dut_m_cost,exp_fire,dut_fire,exp_tokens_after,dut_tokens_after");
        reset_case();
        fill();
        // Backpressure cannot spend tokens or change payload.
        repeat(7) step(0,1,1,1,0,DATA_W'(16'h55aa));
        step(0,1,1,1,1,DATA_W'(16'h55aa));
        // Exercise all payload bits, including DATA_W=64 high half.
        reset_case(); fill();
        step(0,1,1,1,1,{DATA_W{1'b1}});
        step(0,1,1,1,1,(DATA_W'(1) << (DATA_W-1)));
        step(0,1,1,1,1,DATA_W'(64'haaaaaaaaaaaaaaaa));
        step(0,1,1,1,1,DATA_W'(64'h5555555555555555));
        // Capacity-sized request, then same-edge refill used from empty.
        reset_case(); fill();
        step(0,0,1,CAPACITY,1,DATA_W'(1));
        step(0,1,1,REFILL,1,DATA_W'(2));
        // Error injection. Zero and representable over-capacity costs block.
        reset_case(); fill();
        step(0,1,1,0,1,DATA_W'(3));
        if (CAPACITY < ((1<<CW)-1)) step(0,0,1,CAPACITY+1,1,DATA_W'(4));
        // Reset cancels an otherwise valid stalled item.
        reset_case(); fill();
        step(0,0,1,1,0,DATA_W'(5));
        reset_case();
        // Exhaustive one-step state/input combinations for small capacity.
        if(CAPACITY<=17) begin
            for(integer state=0;state<=CAPACITY;state=state+1)
            for(integer t=0;t<2;t=t+1)
            for(integer v=0;v<2;v=v+1)
            for(integer r=0;r<2;r=r+1)
            for(integer c=0;c<(1<<CW);c=c+1) begin
                reset_case(); fill();
                if(state<CAPACITY) step(0,0,1,CAPACITY-state,1,DATA_W'(6));
                step(0,t!=0,v!=0,c,r!=0,DATA_W'(state*31+c));
            end
        end
        reset_case(); rv=0; rc=1; rd='0; last_accept=1;
        // Legal producer: hold an unaccepted request unchanged.
        for(integer n=0;n<6000;n=n+1) begin
            if(!rv || last_accept) begin
                rv=((next_random()%4)!=0);
                rc=1+(next_random()%CAPACITY); rd=next_payload();
            end
            rt=((next_random()%3)!=0); rr=((next_random()%4)!=0);
            step(0,rt,rv,rc,rr,rd);
        end
        // Drain any held input with a bounded completion check.
        for(integer n=0;n<CAPACITY+3 && rv;n=n+1) begin
            step(0,1,rv,rc,1,rd); if(last_accept) rv=0;
        end
        if(rv) $fatal(1,"ASSERT_TOKEN_DRAIN");
        $display("PASS token_bucket checks=%0d transfers=%0d CAPACITY=%0d REFILL=%0d INITIAL=%0d DATA_W=%0d",checks,transfers,CAPACITY,REFILL,INITIAL_TOKENS,DATA_W);
        $fclose(trace_fd);
        $finish;
    end
    initial begin #100000000; $fatal(1,"ASSERT_TOKEN_TIMEOUT"); end
endmodule

9. 从空目录复现默认配置

把以上两个文件放在同一目录,进入已配置好的 Vivado 命令行环境,运行:

xvlog --sv token_bucket.sv tb_token_bucket.sv
xelab tb_token_bucket --timescale 1ns/1ps --debug typical -s token_bucket_sim
xsim token_bucket_sim -runall

本次完整批跑的默认配置原始输出为:

PASS token_bucket checks=10081 transfers=2985 CAPACITY=7 REFILL=3 INITIAL=0 DATA_W=16

其中 checks 是自检周期数,每次测试台 step 增加一次,不是该周期内全部条件断言的总执行次数。transfers 是这些测试场景中累计接受的请求数。该配置同时生成实际 trace.csv。 xelab 创建仿真快照、xsim 执行快照的流程可查 AMD UG900。文档版本与将来实际测试版本需分开记录。

源码包的 run_rtl.py 不依赖第三方 Python 库。默认只显示计划;只有加 --execute --engine xsim 才运行。令牌桶部分的6组正例、6个变异和8个非法参数用例本次全部完成,每例独立目录保留日志。只运行上面的默认配置,不等于复现全部矩阵。

CAPACITYREFILL初始信用DATA_W实际自检周期实际接受请求
110162152908
212866842428
73016100812985
88832128244292
175464472726727
655356553501660393192

独立测试台另测了 (C,R,I,W)=(1,1,0,1)、(65535,32768,32767,64)、(11,4,3,64),共175,621次断言,并实际捕获“valid依赖ready”“消费后再饱和”两个行为变异和REFILL=0参数负例。独立CSV审计按实际输入与DUT观测重算信用,不以exp_*列作答案;两篇共10个正例的137,523行实际记录全部通过逐行审计。CSV后处理不是额外一次RTL仿真,不叠加到原生断言数。

本次原始运行曾因核心与测试台的时间单位设置差异在展开阶段失败;仅修复运行器的时间单位、版本识别和证据采集后,完整回归通过。没有为使测试通过而更改RTL行为或测试激励。最终运行使用显式--timescale 1ns/1ps,并保存真实DUT信号的WDB和CSV。行为仿真通过不能推断实际Fmax、资源数量或板级吞吐率,也不构成形式化证明。

接入自己的链路前,先回答三个具体问题:一个信用代表什么,复位后允许多少突发,非法成本由谁处理。把这三点和同拍顺序写清楚,令牌桶才有确定的系统含义。

附录:完整批跑器

下面是本次实际使用的 run_rtl.py,只依赖Python标准库,要求Python 3.10或更新版本;本次运行使用Python 3.14.5。将它放到工作根目录,把本文两份SV文件放入 token_bucket/ 子目录。运行下面命令复现本文所属的全部正向、变异和非法参数配置:

python run_rtl.py --execute --engine xsim --only token

如果两个题目的SV目录都已放齐,去掉--only即可运行两题共30项。筛选运行的结果会明确标记full_suite=false,不应把单题回归写成两题全部完成。--engine iverilog仅是备选入口,本次结果来自XSim,不宣称Icarus交叉验证已运行。

各项独立目录保存真实命令、UTC起止时间、stdout/stderr、退出状态、编译源哈希与WDB;正向用例另保存完整trace.csv。异常退出、超时、没有真实DUT波形对象、指定变异被其他断言击中,都会影响验收。版本检查必须识别实际工具版本。

#!/usr/bin/env python3
"""Native simulation runner. Default is plan-only; use --execute deliberately.
No third-party Python dependencies. Run in a Vivado environment with xvlog,
xelab and xsim on PATH. Icarus is an optional second simulator, not XSim evidence.
Compilation failure NEVER counts as a killed mutant or invalid-parameter pass.
"""
from pathlib import Path
import argparse, hashlib, json, os, re, shutil, subprocess, sys, datetime

ROOT=Path(__file__).resolve().parent
TOKEN_PARAMS=[
    {'CAPACITY':1,'REFILL':1,'INITIAL_TOKENS':0,'DATA_W':1},
    {'CAPACITY':2,'REFILL':1,'INITIAL_TOKENS':2,'DATA_W':8},
    {'CAPACITY':7,'REFILL':3,'INITIAL_TOKENS':0,'DATA_W':16},
    {'CAPACITY':8,'REFILL':8,'INITIAL_TOKENS':8,'DATA_W':32},
    {'CAPACITY':17,'REFILL':5,'INITIAL_TOKENS':4,'DATA_W':64},
    {'CAPACITY':65535,'REFILL':65535,'INITIAL_TOKENS':0,'DATA_W':16},
]
MUTATIONS={
 'token_bucket':{
  'wrap_credit':('assign available=(raw_credit > (CW+1)\'(CAPACITY)) ? CW\'(CAPACITY) : raw_credit[CW-1:0];','assign available=raw_credit[CW-1:0];'),
  'old_credit':('(s_cost <= available)','(s_cost <= tokens)'),
  'spend_on_valid':('else if(m_valid && m_ready)','else if(m_valid)'),
  'allow_zero':("(s_cost != '0) && ",''),
  'spend_before_saturate':('tokens <= available-s_cost;',"tokens <= ((raw_credit-{1'b0,s_cost}) > (CW+1)'(CAPACITY)) ? CW'(CAPACITY) : CW'(raw_credit-{1'b0,s_cost});"),
  'valid_depends_ready':('assign m_valid=!rst && s_valid && can_send;','assign m_valid=!rst && s_valid && can_send && m_ready;'),
 },
 'stream_fork2':{
  'clear_both_on_any':('assign remaining=pending & ~{m1_ready,m0_ready};',"assign remaining=(|(pending & {m1_ready,m0_ready})) ? 2'b00 : pending;"),
  'repeat_early_receiver':('assign remaining=pending & ~{m1_ready,m0_ready};',"assign remaining=(|(pending & ~{m1_ready,m0_ready})) ? pending : 2'b00;"),
  'accept_when_one_ready':("assign s_ready=!rst && (remaining == 2'b00);","assign s_ready=!rst && ((pending == 2'b00) || (|(pending & {m1_ready,m0_ready})));"),
  'clear_overwrites_replace':("                pending <= 2'b11;\n            end","                pending <= 2'b11;\n            end\n            pending <= remaining;"),
 }
}

EXPECTED_MUTATION_DIAGNOSTICS={
 'wrap_credit':'ASSERT_TOKEN_STATE',
 'old_credit':'ASSERT_TOKEN_CONTROL',
 'spend_on_valid':'ASSERT_TOKEN_STATE',
 'allow_zero':'ASSERT_TOKEN_CONTROL',
 'spend_before_saturate':'ASSERT_TOKEN_STATE',
 'valid_depends_ready':'ASSERT_TOKEN_CONTROL',
 'clear_both_on_any':'ASSERT_FORK_CONTROL',
 'repeat_early_receiver':'ASSERT_FORK_CONTROL',
 'accept_when_one_ready':'ASSERT_FORK_CONTROL',
 'clear_overwrites_replace':'ASSERT_FORK_CONTROL',
}


def cases():
    result=[]
    for i,p in enumerate(TOKEN_PARAMS):
        result.append({'id':f'token_positive_{i}','module':'token_bucket','kind':'positive','parameters':p})
    for w in (1,8,16,64):
        result.append({'id':f'fork_positive_w{w}','module':'stream_fork2','kind':'positive','parameters':{'DATA_W':w}})
    for module,mutants in MUTATIONS.items():
        for mutation in mutants:
            result.append({'id':f'{module}_{mutation}','module':module,'kind':'mutation','mutation':mutation,'parameters':{}})
    for key,value in [('CAPACITY',0),('CAPACITY',65536),('REFILL',0),('REFILL',8),('INITIAL_TOKENS',-1),('INITIAL_TOKENS',8),('DATA_W',0),('DATA_W',65)]:
        result.append({'id':f'token_invalid_{key}_{value}','module':'token_bucket','kind':'invalid','parameters':{key:value}})
    for w in (0,65):
        result.append({'id':f'fork_invalid_w{w}','module':'stream_fork2','kind':'invalid','parameters':{'DATA_W':w}})
    return result


def tool_path(name):
    # Vivado Windows commonly exposes .bat wrappers.
    value=shutil.which(name) or shutil.which(name+'.bat')
    if not value:
        raise RuntimeError(f'{name} not found on PATH; native stage remains NOT RUN')
    return value


def run_command(command, cwd, label, timeout=120):
    started=datetime.datetime.now(datetime.timezone.utc).isoformat()
    timed_out=False
    try:
        result=subprocess.run(command,cwd=cwd,stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,timeout=timeout,shell=False)
        stdout=result.stdout; stderr=result.stderr
        code=result.returncode
    except subprocess.TimeoutExpired as error:
        timed_out=True
        stdout=error.stdout or b''; stderr=error.stderr or b''
        code=124
    (cwd/(label+'.stdout.log')).write_bytes(stdout)
    (cwd/(label+'.stderr.log')).write_bytes(stderr)
    text=stdout.decode('utf-8',errors='replace')+stderr.decode('utf-8',errors='replace')
    if timed_out:text+='\nRUNNER_TIMEOUT\n'
    (cwd/(label+'.log')).write_text(text,encoding='utf-8',newline='\n')
    record={'command':command,'cwd':str(cwd),'start_utc':started,
            'end_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),
            'exit_code':code,'timed_out':timed_out,'log':str(cwd/(label+'.log')),
            'stdout':str(cwd/(label+'.stdout.log')),'stderr':str(cwd/(label+'.stderr.log'))}
    (cwd/(label+'.command.json')).write_text(json.dumps(record,indent=2)+'\n',encoding='utf-8',newline='\n')
    return record,text


def prepare(case, folder):
    module=case['module']
    source=(ROOT/module/(module+'.sv')).read_text(encoding='utf-8')
    if case['kind']=='mutation':
        old,new=MUTATIONS[module][case['mutation']]
        if source.count(old)!=1:raise RuntimeError(f"mutation anchor not unique: {case['id']}")
        source=source.replace(old,new)
    (folder/(module+'.sv')).write_text(source,encoding='utf-8',newline='\n')
    params=', '.join(f'.{key}({value})' for key,value in case['parameters'].items())
    parameter_clause=f'#({params})' if params else ''
    if case['kind']=='invalid':
        wrapper=f'''`timescale 1ns/1ps
module tb_case;
  {module} {parameter_clause} dut();
  initial begin #10; $fatal(1,"INVALID_PARAMETER_ACCEPTED"); end
endmodule
'''
        sources=[module+'.sv','tb_case.sv']
    else:
        tb='tb_'+module
        shutil.copy2(ROOT/module/(tb+'.sv'),folder/(tb+'.sv'))
        wrapper=f'module tb_case; {tb} {parameter_clause} test(); endmodule\n'
        sources=[module+'.sv',tb+'.sv','tb_case.sv']
    (folder/'tb_case.sv').write_text(wrapper,encoding='utf-8',newline='\n')
    hashes={name:hashlib.sha256((folder/name).read_bytes()).hexdigest() for name in sources}
    (folder/'compiled-source-hashes.json').write_text(json.dumps(hashes,indent=2)+'\n',encoding='utf-8',newline='\n')
    return sources


def execute(case, run_root, engine, executables):
    folder=run_root/case['id'];folder.mkdir()
    sources=prepare(case,folder)
    logs=[]
    if engine=='xsim':
        compile_commands=[('xvlog',[executables['xvlog'],'--sv',*sources]),
                          ('xelab',[executables['xelab'],'tb_case','--timescale','1ns/1ps','--debug','typical','-s','sim'])]
        scope='/tb_case/dut/*' if case['kind']=='invalid' else '/tb_case/test/dut/*'
        tcl=f'set objects [get_objects {scope}]\nif {{[llength $objects] == 0}} {{error "WAVE_SCOPE_EMPTY"}}\nputs "WAVE_OBJECTS=[llength $objects]"\nlog_wave $objects\nrun all\nquit\n'
        (folder/'run.tcl').write_text(tcl,encoding='utf-8',newline='\n')
        sim_command=[executables['xsim'],'sim','--tclbatch','run.tcl','--wdb','simulation.wdb']
    else:
        compile_commands=[('iverilog',[executables['iverilog'],'-g2012','-s','tb_case','-o','sim.vvp',*sources])]
        sim_command=[executables['vvp'],'sim.vvp']
    for label,command in compile_commands:
        info,text=run_command(command,folder,label)
        logs.append(info)
        if info['exit_code']!=0:
            return {**case,'result':'FAIL_COMPILE','commands':logs}
    info,text=run_command(sim_command,folder,'simulation')
    logs.append(info)
    # A diagnostic printed before a timeout/crash is not a completed test.
    process_ok=(not info.get('timed_out',False) and info['exit_code'] in (0,1)
                and 'RUNNER_TIMEOUT' not in text and 'TRACE_OPEN_ERROR' not in text)
    if engine=='xsim':
        # Tcl echoes source lines: inspect the emitted count, not an error string
        # that also appears in an unexecuted guard's echoed source.
        process_ok=process_ok and bool(re.search(r'^WAVE_OBJECTS=[1-9]\d*\s*$',text,re.M))
    if not process_ok:
        status='FAIL_EXECUTION'
    elif case['kind']=='positive':
        ok=(info['exit_code']==0 and f"PASS {case['module']} " in text
            and 'ASSERT_' not in text and 'PARAM_ERROR' not in text)
        status='PASS_NATIVE' if ok else 'FAIL_NATIVE'
    elif case['kind']=='mutation':
        expected=EXPECTED_MUTATION_DIAGNOSTICS[case['mutation']]
        # A different assertion is a test-design issue, not the expected kill.
        diagnostics=set(re.findall(r'ASSERT_[A-Z0-9_]+',text))
        ok=(diagnostics=={expected} and f"PASS {case['module']} " not in text
            and 'PARAM_ERROR' not in text)
        status='KILLED_NATIVE' if ok else 'SURVIVED_OR_INVALID_FAILURE'
    else:
        ok=(f"PARAM_ERROR {case['module']}" in text
            and 'INVALID_PARAMETER_ACCEPTED' not in text and 'PASS ' not in text
            and 'ASSERT_' not in text)
        status='REJECTED_NATIVE' if ok else 'FAIL_PARAMETER_CHECK'
    return {**case,'result':status,'commands':logs}


def main():
    parser=argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--engine',choices=['xsim','iverilog'],default='xsim')
    parser.add_argument('--execute',action='store_true')
    parser.add_argument('--only',default='',help='optional case-id substring')
    parser.add_argument('--output',type=Path,default=ROOT/'native_runs')
    args=parser.parse_args()
    selected=[c for c in cases() if args.only in c['id']]
    if not selected:raise SystemExit('No matching cases')
    if not args.execute:
        print(json.dumps({'status':'NOT RUN','engine':args.engine,'cases':selected},ensure_ascii=False,indent=2))
        return
    names=['xvlog','xelab','xsim'] if args.engine=='xsim' else ['iverilog','vvp']
    executables={name:tool_path(name) for name in names}
    stamp=datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%S.%fZ')
    run_root=args.output.resolve()/f'{args.engine}_{stamp}';run_root.mkdir(parents=True)
    versions=[]
    for name,exe in executables.items():
        option='-V' if name in ('iverilog','vvp') else '-version'
        record,version_text=run_command([exe,option],run_root,name+'_version',30)
        pattern=r'(?<!\d)(?:19|20)\d{2}\.\d+(?:\.\d+)?\b' if args.engine=='xsim' else r'(?i)\bversion\s+\d+'
        record['version_identified']=bool(record['exit_code']==0 and not record.get('timed_out',False) and re.search(pattern,version_text))
        versions.append(record)
    hashes={str(p.relative_to(ROOT)):hashlib.sha256(p.read_bytes()).hexdigest()
            for p in sorted(ROOT.rglob('*.sv')) if 'native_runs' not in p.parts}
    results=[execute(case,run_root,args.engine,executables) for case in selected]
    okay={'PASS_NATIVE','KILLED_NATIVE','REJECTED_NATIVE'}
    report={'engine':args.engine,'utc_start':stamp,'versions':versions,'source_sha256':hashes,
            'result':('FAIL_VERSION_EVIDENCE' if not all(v['version_identified'] for v in versions)
                      else 'PASS_SELECTED_NATIVE' if all(r['result'] in okay for r in results) else 'FAIL_NATIVE'),
            'full_suite':len(selected)==len(cases()),'cases':results,
            'limits':'RTL behavioral simulation only. No synthesis, implementation, board or timing evidence.'}
    report['utc_end']=datetime.datetime.now(datetime.timezone.utc).isoformat()
    report['runner_sha256']=hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
    (run_root/'results.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n',encoding='utf-8',newline='\n')
    print(json.dumps(report,ensure_ascii=False,indent=2))
    if report['result']!='PASS_SELECTED_NATIVE':raise SystemExit(1)


if __name__=='__main__':main()
【信息科学与工程学】【运营科学】第二篇 C4信息与通信网络运营 (C4) ——数据中心网络运营06 13 | 司法诉讼 | 原告律师 | 争取和解优势 | 证据披露的序贯博弈 | 策略性证据释放 | 持有证据集E = {强证据e_s, 中证据e_m, 弱证据e_w}。设公众原有认知人格向量为 Pold​​=(p1​,p2​,...,pk​), 其中p_i为负面特质(如“不敬业”)。危机后, 通过行为序列 {At​}和宣传, 将公众认知向新向量 Pnew​​引导, 使得在关键维度上, Pnew​​与 Pold​​的余弦相似度降低, 并与一个正面的、可接受的“原型”(如“浪子回头”、“慈父”)对齐。 阅读详情

相关推荐

【信息科学与工程学】【数据中心】第三十二篇 云数据中心的计算/存储/网络的核心挑战01

状态方程: SOCk+1​=SOCk​−Qn​ik​Δt​;: 三相电流变换到αβ坐标系: iα​=32​​(ia​−21​ib​−21​ic​), iβ​=32​​(23​​ib​−23​​ic​). 计算瞬时有功功率p和无功功率q,经低通滤波器分离直流分量,反变换得基波电流,差值即为谐波指令。: 节点i的信念 bi​(xi​)=ϕi​(xi​)∏j∈N(i)​mji​(xi​), 消息 mij​(xj​)=∑xi​​ϕi​(xi​)ψij​(xi​,xj​)∏k∈N(i)\j​mki​(xi​).

weixin_49199313的博客 217

【信息科学与工程学】【通信工程】 第九十二 OTN网络01各类函数02

​:Chaos Mesh 在资源效率与响应速度上优势明显,适合高频次、低延迟实验;LitmusChaos 因多层调度机制,更适合复杂编排场景。

weixin_49199313的博客 1140

【信息科学与工程学】【通信工程】第一百五十五篇 网络拓扑学05

编号类型领域问题问题的数学分析(拓扑学+其他)参数列表及参数的数值范围关联知识798图论/组合优化数据中心网络拓扑如何用CXL 3.0的Host-Managed Device Memory (HDM) 优化内存扩展?​1. 拓扑学分析: - CXL 3.0 HDM允许主机将CXL设备的内存作为系统内存的一部分进行管理。主机通过BIOS或操作系统将设备内存添加到NUMA节点,应用程序透明地使用扩展内存,无需修改。 - HDM模型: * 设备内存通过CXL.mem协议映射到主机的物理地址空间。

weixin_49199313的博客 53

FPGA与ASIC的区别详解:内部结构、开发成本与适用场景,项目选型到底该选哪个

FPGA与ASIC是数字芯片的两条根本路线:FPGA用可编程的LUT、触发器与布线网络在制造后还能改硬件,ASIC用标准单元把电路一次固化在硅上。本文讲清两者的内部结构、性能与功耗差距、NRE与单颗成本的交叉点,以及原型、小批量与大规模量产各自该选哪个,并回答GPU和CPU算不算ASIC这个常见问题。

sindwe的博客 334

【信息科学与工程学】计算机科学与技术——第二百二十五篇 架构设计 03 云原生架构02

启用HPA Controller Partial Cache(只缓存必要字段)、调整Informer Resync Period减少内存、启用Controller Memory Limit、监控Informer Cache内存。: 设置HPA stabilizationWindowSeconds大于CA扩容时间、启用CA scale-down-delay-after-add、使用PriorityBased策略优先保留Pod、监控HPA-CA震荡频率。模型并行allreduce需5TB/s→满足;

weixin_49199313的博客 350

【信息科学与工程学】计算机科学与自动化-——第十五篇云计算 12 公有云里的“多Region + 多AZ“ 02 运营算法01

算法逐步思考推理思考(含数学方程式、对象、资源、任务、进程/协程/线程及对应的代数、约束、目标函数/传递函数/依赖函数、参数及参数的数值范围及边界条件、逐步推理的数学方程式列表、上下文切换、并发/串行/随机/乱序/顺序)算法逐步思考推理思考(含数学方程式、对象、资源、任务、进程/协程/线程及对应的代数、约束、目标函数/传递函数/依赖函数、参数及参数的数值范围及边界条件、逐步推理的数学方程式列表、上下文切换、并发/串行/随机/乱序/顺序)云厂商提高闲置资源利用率。算法复杂度为O(N),N为实例数量。

weixin_49199313的博客 279

FPGA学习(一)——LED点灯与开发流程

本文介绍了FPGA的基本概念及其在数字信号处理、通信、工业控制等领域的应用优势,强调其灵活性、并行性和集成性,同时指出其设计复杂度高、功耗较大等局限。随后以LED点灯为例,详细演示了基于Vivado的FPGA开发流程:创建工程、配置引脚约束、生成比特流文件,并通过仿真验证设计。重点展示了如何添加调试探针、连接硬件并实时观察信号,为初学者提供了完整的FPGA入门实践指南。

weixin_55136196的博客 82

《Agent实战FPGA+MCP+skill》课时2.8|Testbench 零基础全自动生成

本课聚焦Testbench零基础自动生成,系统讲解行为级、门级与时序级仿真差异,构建包含时钟复位生成、激励驱动、监测控制的标准Testbench结构。通过$display、$monitor、$finish实现检查点判定,确立仿真作为AI代码验证的“裁判”地位。结合tb-flow技能闭环,实现从DUT到全覆盖仿真的自动化流程,确保复位、边界、抑制等关键场景均被验证,为FPGA工程提供可信赖的验证基石。

现任某外企的算法工程师!专注于人工智能、嵌入式、FPGA、论文写作四大主题的教程和经验分享!辅导请➕v :sci666888999 33

FPGA 帧长监视器:背压时不计数,过长时不回绕

用 SystemVerilog 构建被动帧长监视器,只统计 valid 与 ready 同时有效的传输,在接受 last 时报告短帧、正常帧或长帧。计数饱和避免超长回绕,附完整 RTL、测试台与复现脚本,并给出原生 XSim 及独立复核结果。

hxy_programmer的博客 437

《Agent实战FPGA+MCP+skill》课时2.7|CDC 跨时钟域处理

本课系统讲解FPGA跨时钟域(CDC)设计,涵盖时钟管理(MMCM/PLL)、亚稳态成因与规避、两级同步器与脉冲同步电路设计、异步FIFO格雷码指针机制及CDC审查红线。通过Clock Wizard配置时钟、构建两级同步器防抖、实现快转慢脉冲同步,并结合cdc_audit工作流完成从建工程、双时钟仿真到ILA验证的完整闭环,掌握CDC设计核心技能与工程实践规范。

现任某外企的算法工程师!专注于人工智能、嵌入式、FPGA、论文写作四大主题的教程和经验分享!辅导请➕v :sci666888999 473

excel 利用SUBTOTAL函数计算筛选后数列总和、均值、最大、最小值

表里有三个人,咱们需要筛选出三个人,并分别计算他们的总和值。 先看一下数据范围(A1:B27) 通过筛选找张三。 按照上图所示输入函数,选择9,求和。 然后按照函数输入即可。 就能计算出结果。 筛选项选择王五,就会自动计算王五的总和。 筛选到李四就会自动计算李四的总和。 如果把9改成1,就是自动计算筛选出来的均值结果,如下图所示。 如果改成4就是取筛选项的最大值,选择5就是取最小值。 ...

yuanYW7556的博客 3万+

FPGA 16 位 LFSR:先固定移位约定,再用 XSim 验证周期与暂停

固定一种 16 位右移 Galois LFSR,明确反馈掩码、种子、同步复位、装载与使能优先级。用原生 Vivado XSim 检查三种种子的 65535 状态周期、暂停和采样时刻,并通过独立测试台与故障变体验证。

hxy_programmer的博客 162

【驱动适配】高通sa522平台tac5x1x驱动适配全指南

本手册详细介绍了TAC5301-Q1音频编解码器在高通sa525平台Linux系统下的驱动适配流程。主要内容包括:1)TAC5301-Q1核心特性(车载级音频Codec、关键接口和功能);2)驱动适配依赖(高通声卡驱动、ALSA SoC框架等);3)I2S协议原理及主从模式说明;4)硬件准备与检查要点;5)完整的驱动移植步骤(代码移植、Kconfig/Makefile修改、设备树配置)。该手册适用于嵌入式音频开发工程师快速完成TAC5301-Q1芯片的驱动移植与验证工作。

嵌入式博哥的博客 302

产业趋势:计算存储

下面把 Samsung SmartSSD、ScaleFlux、Pliops、Eideticom​ 放在「计算存储 / 数据存储加速」同一坐标系里做技术跟踪与对比。四家都解决“数据别再搬来搬去”,但产品形态、可编程性、目标工作负载、商业化路径完全不同。厂商产品形态是否自带 NAND核心定位关键价值Samsung SmartSSD计算存储盘 CSD是FPGA/ACAP + SSD 同封装把用户算法推到数据旁,通用加速ScaleFlux计算存储盘 CSD是ASIC 存储处理器 + 透明压缩不改应用,提 IOPS

baidu_38316985的博客 173

手把手教你用LTspice仿真BUCK-BOOST电路(附波形分析)

本文详细介绍了如何使用LTspice进行BUCK-BOOST电路的仿真与波形分析,涵盖电路原理、CCM与DCM模式特性、效率优化及常见问题解决方案。通过实战案例,帮助工程师快速掌握升降压转换电路的设计与调试技巧,提升电力电子设计能力。

julia4scientist的博客 1044

影楼精修-肤色统一算法解析

本文介绍了影楼精修中肤色统一算法的实现方案,并以像素蛋糕软件为例进行详细分析。肤色统一旨在通过调整人像照片中皮肤区域的颜色,使其看起来更加均匀一致。像素蛋糕软件提供了AI肤色统一和传统肤色统一两种功能,分别适用于不同需求。传统肤色统一算法通过皮肤分割、人脸检测、亮度统一和颜色统一等步骤,实现了对皮肤区域的精细调整。文章还提供了亮度统一的核心代码,并对比了传统算法与AI算法的效果,指出AI算法在自然度和亮度矫正方面表现更优。整体而言,本文为影楼精修中的肤色统一提供了实用的技术参考。

Trent1985的专栏 2059

硬件选型笔记:HDMI 转 LVDS/TTL 接收方案 GSV1015S@ACP 评估

本篇记录 GSV1015S HDMI 转 LVDS/TTL 接收芯片规格与工程踩坑点,适合工控屏、医疗显示、FPGA 图像采集项目前期方案评估,重点关注封装便利性、缩放与时序、HDCP 版本带来的兼容性边界。

ACP广源盛的博客 174

NL2SQL进阶系列(3):Data-Copilot、Chat2DB、Vanna Text2SQL优化框架开源应用实践详解[Text2SQL]

NL2SQL进阶系列(3):Data-Copilot、Chat2DB、Vanna Text2SQL优化框架开源应用实践详解[Text2SQL]

丨汀、的博客 5419

LabVIEW CompactRIO 凭什么比工控机稳(系列 01/05)

CompactRIO以实时处理器、FPGA与C系列I/O模块的三层架构,实现确定性、高速与灵活性的统一。实时系统确保任务准时完成,抖动极小;FPGA绕过总线,实现微秒级响应与自定义逻辑;可互换模块直接对接工业信号,简化接线。三者分工明确:FPGA“快而小”,实时处理器“算得多”,主机“给人看”。适用于高可靠性控制场景,如大型对撞机、发动机仿真等。理解此架构是后续编程与系统设计的基础。

bjcyck的博客 657

YOLOv11涨点改进 | Conv篇/小目标改进 | YOLOv11引入SPDConv卷积特征无损下采样模块,增加YOLOv11小目标P2检测层(助力小目标任务高效涨点)

本文提出了一种创新的SPDConv模块来优化YOLOv11模型,通过空间到深度(SPD)层和非步幅卷积层实现特征无损下采样。该方法有效解决了传统CNN中因跨步卷积和池化层导致的细粒度信息丢失问题,特别提升了小目标检测性能。实验表明,SPDConv在目标检测和图像分类任务中显著优于现有SOTA模型。文章详细介绍了模块原理、核心代码实现,并提供了YOLOv11集成SPDConv的具体方法,包括配置文件修改和P2层增强等优化技巧。该技术为计算机视觉任务提供了一种高效的特征保留方案。

Ai缝合怪、助力小伙伴高效发论文 586

095、PCIE物理层测试模式:从信号眼图到误码率实战

本文介绍了PCIe物理层测试模式的调试方法和实战经验。重点解析了环回测试、合规性测试和静默模式的应用场景及配置要点,分享了信号眼图分析、误码率测试和接收端验证的实用技巧。作者结合自身调试经历,强调物理层测试对排查信号完整性问题的重要性,建议在量产前进行基础测试,并提醒注意芯片勘误和测试模式切换等易错点。通过合理使用测试模式,可显著提高PCIe链路调试效率,确保物理层稳定可靠。

z534437748的博客 59

FPGA数据采集接口选型终极指南

本文针对FPGA高速数据采集系统中的通信接口选型问题,分析了USB、以太网与PCIe三类接口的适用场景与技术要点。重点推荐采用集成UDP硬核协议栈的以太网方案,实现低资源占用、高吞吐稳定性和快速开发;结合时序控制、FIFO缓存与硬件加速,确保数据可靠传输。提出“数据率—部署形态—延迟敏感度”三步选型法,指导工程落地,最终构建高效、稳定的采集系统。

weixin_46897017的博客 460

KylinOS 离线安装 MinIO【图文步骤】

在国产操作系统 KylinOS 中安装 MinIO,详细步骤...

甘蓝的专栏 1534

NI PXIe‑5820 PXIe-5860 PXIe-5841基带 PXI 矢量信号收发仪

NI PXIe-5820是2槽PXIe基带矢量信号收发仪,支持DC~500MHz、1GHz瞬时I/Q带宽,集成FPGA实现实时信号处理、DPD、ET及自定义算法。适用于5G/WiFi6/7、UWB基带芯片验证、功放DPD测试、MIMO基带阵列与通信原型开发。通过差分I/Q接口直连射频前端,需外接上变频模块生成射频信号。具备高带宽、低延迟、多模块同步优势,搭配RFmx与LabVIEW/Python实现自动化测试与开发,是基带IC实验室与产线测试的理想平台。

QH13929231880的博客 375

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

动手第一课:用 Verilog 写计数器并跑通仿真

前面几篇都在讲“地图”和“约束”,这一篇开始动手。目标很具体:用 Verilog 写一个可综合的计数器,写一个 testbench 验证它,跑仿真、看波形,最后用一个 Makefile 把整个流程串起来。这条链路是芯片设计中最小的可运行闭环,也是后续所有复杂设计的骨架。工具选择上,本文使用:Icarus Verilog(仿真)+ GTKWave(波形)+ Yosys(综合)。选择开源工具不是因为商业工具不好,而是因为它们。

oscar999的专栏 189

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

FPGA学习(二)——LED闪烁

本文介绍了Verilog中的组合逻辑与时序逻辑应用,重点实现LED闪烁实验。通过三级分频(50MHz→1MHz→1kHz→1Hz)生成不同频率信号,控制LED以1Hz频率闪烁,并对比了两种设计方案:方案一采用多级时钟分频,方案二统一在50MHz时钟域内,通过26位计数器直接生成1Hz使能信号,简化时序设计、减少资源占用。仿真验证了各阶段波形正确性,确保系统稳定工作。

weixin_55136196的博客 150

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

上一篇: MATLAB 二进制采集记录怎么读:字节序、二补码与截断帧的严格检查
下一篇: MATLAB 大动态范围求和:用 Neumaier 补偿保留小增量,但别把它当精确算术
星夜离尘56
博客等级 码龄1年 77粉丝 100原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值