文章目录

  • Testbench结构
  • 划分测试功能点
  • 仿真命令

之前学习的一些验证要素: 验证环境按隔离的概念,分为硬件DUT,软件testbench和接口interface; 验证阶段分为建立阶段(build),连接阶段(connect),产生激励阶段(generator)和发送激励阶段(transfer)。
MCDF子系统相比MCDT添加了数据整形、寄存器控制和状态显示功能。整个验证环境各组件之间相互独立,依靠event和mailbox同步和通信。

Testbench结构

  • reg_pkg构造与chnl_pkg类似,initiator变为driver,generator数据将发给driver,monitor数据发给checker
  • fmt_agent作为responsor,被动接收数据
package fmt_pkg;import rpt_pkg::*;typedef enum {SHORT_FIFO, MED_FIFO, LONG_FIFO, ULTRA_FIFO} fmt_fifo_t;  // 定义FIFO容量typedef enum {LOW_WIDTH, MED_WIDTH, HIGH_WIDTH, ULTRA_WIDTH} fmt_bandwidth_t;  // 定义带宽class fmt_trans;rand fmt_fifo_t fifo;rand fmt_bandwidth_t bandwidth;bit [9:0] length;bit [31:0] data[];bit [1:0] ch_id;bit rsp;constraint cstr{soft fifo == MED_FIFO;soft bandwidth == MED_WIDTH;};function fmt_trans clone();...function string sprint();...function bit compare(fmt_trans t);  // 比较另一个对象string s;compare = 1;s = "\n=======================================\n";s = {s, $sformatf("COMPARING fmt_trans object at time %0d \n", $time)};if(this.length != t.length) begincompare = 0;s = {s, $sformatf("sobj length %0d != tobj length %0d \n", this.length, t.length)};endif(this.ch_id != t.ch_id) begincompare = 0;s = {s, $sformatf("sobj ch_id %0d != tobj ch_id %0d\n", this.ch_id, t.ch_id)};endforeach(this.data[i]) beginif(this.data[i] != t.data[i]) begincompare = 0;s = {s, $sformatf("sobj data[%0d] %8x != tobj data[%0d] %8x\n", i, this.data[i], i, t.data[i])};endendif(compare == 1) s = {s, "COMPARED SUCCESS!\n"};else  s = {s, "COMPARED FAILURE!\n"};s = {s, "=======================================\n"};rpt_pkg::rpt_msg("[CMPOBJ]", s, rpt_pkg::INFO, rpt_pkg::MEDIUM);endfunctionendclassclass fmt_driver;local string name;local virtual fmt_intf intf;mailbox #(fmt_trans) req_mb;mailbox #(fmt_trans) rsp_mb;local mailbox #(bit[31:0]) fifo;local int fifo_bound;         // FIFO最大长度local int data_consum_peroid;    // 数据消耗周期function new(string name = "fmt_driver");this.name = name;this.fifo = new();this.fifo_bound = 4096;this.data_consum_peroid = 1;endfunctionfunction void set_interface(virtual fmt_intf intf);if(intf == null)$error("interface handle is NULL, please check if target interface has been intantiated");elsethis.intf = intf;endfunctiontask run();fork  // 模拟硬件并行this.do_receive();this.do_consume();this.do_config();this.do_reset();joinendtasktask do_config();  // 从fmt_driver获取配置fmt_trans req, rsp;forever beginthis.req_mb.get(req);case(req.fifo)SHORT_FIFO: this.fifo_bound = 64;MED_FIFO: this.fifo_bound = 256;LONG_FIFO: this.fifo_bound = 512;ULTRA_FIFO: this.fifo_bound = 2048;endcasethis.fifo = new(this.fifo_bound);case(req.bandwidth)LOW_WIDTH: this.data_consum_peroid = 8;MED_WIDTH: this.data_consum_peroid = 4;HIGH_WIDTH: this.data_consum_peroid = 2;ULTRA_WIDTH: this.data_consum_peroid = 1;endcasersp = req.clone();rsp.rsp = 1;this.rsp_mb.put(rsp);endendtasktask do_reset();...task do_receive();  // 接收数据forever begin@(posedge intf.fmt_req);forever begin@(posedge intf.clk);if((this.fifo_bound-this.fifo.num()) >= intf.fmt_length)  //等待余量大于长度break;endintf.drv_ck.fmt_grant <= 1;@(posedge intf.fmt_start);forkbegin@(posedge intf.clk);  // 次拍grant变为0intf.drv_ck.fmt_grant <= 0;endjoin_nonerepeat(intf.fmt_length) begin  // 重复length次@(negedge intf.clk);this.fifo.put(intf.fmt_data);  // 采样数据endendendtasktask do_consume();  // 消耗数据bit[31:0] data;forever beginvoid'(this.fifo.try_get(data));repeat($urandom_range(1, this.data_consum_peroid)) @(posedge intf.clk);endendtaskendclassclass fmt_generator;rand fmt_fifo_t fifo = MED_FIFO;rand fmt_bandwidth_t bandwidth = MED_WIDTH;mailbox #(fmt_trans) req_mb;mailbox #(fmt_trans) rsp_mb;constraint cstr{soft fifo == MED_FIFO;soft bandwidth == MED_WIDTH;}function new();this.req_mb = new();this.rsp_mb = new();endfunctiontask start();send_trans();endtask// generate transaction and put into local mailboxtask send_trans();fmt_trans req, rsp;req = new();assert(req.randomize with {local::fifo != MED_FIFO -> fifo == local::fifo; local::bandwidth != MED_WIDTH -> bandwidth == local::bandwidth;})else $fatal("[RNDFAIL] formatter packet randomization failure!");$display(req.sprint());this.req_mb.put(req);this.rsp_mb.get(rsp);$display(rsp.sprint());assert(rsp.rsp)else $error("[RSPERR] %0t error response received!", $time);endtaskfunction string sprint();...function void post_randomize();string s;s = {"AFTER RANDOMIZATION \n", this.sprint()};$display(s);endfunctionendclassclass fmt_monitor;...class fmt_agent;  // 盒子,例化组件,调用各组件run()...endpackage
  • mcdf_pkg实现mcdf_checker的功能,需要通过reference model模拟硬件功能完成比对
`include "param_def.v"package mcdf_pkg;import chnl_pkg::*;import reg_pkg::*;import arb_pkg::*;import fmt_pkg::*;import rpt_pkg::*;typedef struct packed {bit[2:0] len;bit[1:0] prio;bit en;bit[7:0] avail;} mcdf_reg_t;typedef enum {RW_LEN, RW_PRIO, RW_EN, RD_AVAIL} mcdf_field_t;class mcdf_refmod;local virtual mcdf_intf intf;local string name;mcdf_reg_t regs[3];mailbox #(reg_trans) reg_mb;mailbox #(mon_data_t) in_mbs[3];mailbox #(fmt_trans) out_mbs[3];function new(string name="mcdf_refmod");this.name = name;foreach(this.out_mbs[i]) this.out_mbs[i] = new();endfunctiontask run();forkdo_reset();this.do_reg_update();do_packet(0);do_packet(1);do_packet(2);joinendtasktask do_reg_update();  // 捕获数据并更新reg_trans t;forever beginthis.reg_mb.get(t);if(t.addr[7:4] == 0 && t.cmd == `WRITE) beginthis.regs[t.addr[3:2]].en = t.data[0];this.regs[t.addr[3:2]].prio = t.data[2:1];this.regs[t.addr[3:2]].len = t.data[5:3];endelse if(t.addr[7:4] == 1 && t.cmd == `READ) beginthis.regs[t.addr[3:2]].avail = t.data[7:0];endendendtasktask do_packet(int id);  // 模拟打包逻辑fmt_trans ot;mon_data_t it;bit[2:0] len;forever beginthis.in_mbs[id].peek(it);ot = new();len = this.get_field_value(id, RW_LEN);ot.length = len > 3 ? 32 : 4 << len;ot.data = new[ot.length];ot.ch_id = id;foreach(ot.data[m]) beginthis.in_mbs[id].get(it);ot.data[m] = it.data;endthis.out_mbs[id].put(ot);endendtaskfunction int get_field_value(int id, mcdf_field_t f);  // 模拟寄存器行为case(f)RW_LEN: return regs[id].len;RW_PRIO: return regs[id].prio;RW_EN: return regs[id].en;RD_AVAIL: return regs[id].avail;endcaseendfunction task do_reset();forever begin@(negedge intf.rstn); foreach(regs[i]) beginregs[i].len = 'h0;regs[i].prio = 'h3;regs[i].en = 'h1;regs[i].avail = 'h20;endforeach(out_mbs[i]) out_mbs[i].delete();  // 清空mailbox,也可用new()endendtaskfunction void set_interface(virtual mcdf_intf intf);if(intf == null)$error("interface handle is NULL, please check if target interface has been intantiated");elsethis.intf = intf;endfunctionendclassclass mcdf_checker;local string name;local int err_count;local int total_count;local int chnl_count[3];local virtual mcdf_intf intf;local mcdf_refmod refmod;mailbox #(mon_data_t) chnl_mbs[3];mailbox #(fmt_trans) fmt_mb;mailbox #(reg_trans) reg_mb;mailbox #(fmt_trans) exp_mbs[3];  // 在refmod内实例化function new(string name="mcdf_checker");...function void set_interface(virtual mcdf_intf intf);...task run();forkthis.do_compare();this.refmod.run();joinendtasktask do_compare();fmt_trans expt, mont;bit cmp;forever beginthis.fmt_mb.get(mont);this.exp_mbs[mont.ch_id].get(expt);cmp = mont.compare(expt);   // 调用compare()this.total_count++;this.chnl_count[mont.ch_id]++;if(cmp == 0) beginthis.err_count++;rpt_pkg::rpt_msg("[CMPFAIL]", $sformatf("%0t %0dth times comparing but failed! MCDF monitored output packet is different with reference model output", $time, this.total_count),rpt_pkg::ERROR,rpt_pkg::TOP,rpt_pkg::LOG);endelse ...endendtaskfunction void do_report();string s;s = "\n---------------------------------------------------------------\n";s = {s, "CHECKER SUMMARY \n"}; s = {s, $sformatf("total comparison count: %0d \n", this.total_count)}; foreach(this.chnl_count[i]) s = {s, $sformatf(" channel[%0d] comparison count: %0d \n", i, this.chnl_count[i])};s = {s, $sformatf("total error count: %0d \n", this.err_count)}; foreach(this.chnl_mbs[i]) beginif(this.chnl_mbs[i].num() != 0)s = {s, $sformatf("WARNING:: chnl_mbs[%0d] is not empty! size = %0d \n", i, this.chnl_mbs[i].num())}; endif(this.fmt_mb.num() != 0)s = {s, $sformatf("WARNING:: fmt_mb is not empty! size = %0d \n", this.fmt_mb.num())}; s = {s, "---------------------------------------------------------------\n"};rpt_pkg::rpt_msg($sformatf("[%s]",this.name), s, rpt_pkg::INFO, rpt_pkg::TOP);  //调用rpt_pkg()替代$diaplay(),定义了一些消息等级endfunctionendclassclass mcdf_env;  // 例化所有组件...class mcdf_base_test;chnl_generator chnl_gens[3];reg_generator reg_gen;fmt_generator fmt_gen;mcdf_env env;protected string name;function new(string name = "mcdf_base_test");this.name = name;this.env = new("env");foreach(this.chnl_gens[i]) beginthis.chnl_gens[i] = new();this.env.chnl_agts[i].driver.req_mb = this.chnl_gens[i].req_mb;this.env.chnl_agts[i].driver.rsp_mb = this.chnl_gens[i].rsp_mb;endthis.reg_gen = new();this.env.reg_agt.driver.req_mb = this.reg_gen.req_mb;this.env.reg_agt.driver.rsp_mb = this.reg_gen.rsp_mb;this.fmt_gen = new();this.env.fmt_agt.driver.req_mb = this.fmt_gen.req_mb;this.env.fmt_agt.driver.rsp_mb = this.fmt_gen.rsp_mb;rpt_pkg::logname = {this.name, "_check.log"};rpt_pkg::clean_log();$display("%s instantiated and connected objects", this.name);endfunctionvirtual task run();forkenv.run();join_none  // 防止阻塞rpt_pkg::rpt_msg("[TEST]",$sformatf("=====================%s AT TIME %0t STARTED=====================", this.name, $time),rpt_pkg::INFO,rpt_pkg::HIGH);this.do_reg();this.do_formatter();this.do_data();rpt_pkg::rpt_msg("[TEST]",$sformatf("=====================%s AT TIME %0t FINISHED=====================", this.name, $time),rpt_pkg::INFO,rpt_pkg::HIGH);this.do_report();$finish();endtask// do register configurationvirtual task do_reg();endtask// do external formatter down stream slave configurationvirtual task do_formatter();endtask// do data transition from 3 channel slavesvirtual task do_data();endtask// do simulation summaryvirtual function void do_report();this.env.do_report();rpt_pkg::do_report();endfunctionvirtual function void set_interface(...virtual function bit diff_value(int val1, int val2, string id = "value_compare");if(val1 != val2) beginrpt_pkg::rpt_msg("[CMPERR]", $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2), rpt_pkg::ERROR, rpt_pkg::TOP);return 0;endelse beginrpt_pkg::rpt_msg("[CMPSUC]", $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2),rpt_pkg::INFO,rpt_pkg::HIGH);return 1;endendfunctionvirtual task idle_reg();void'(reg_gen.randomize() with {cmd == `IDLE; addr == 0; data == 0;});reg_gen.start();endtaskvirtual task write_reg(bit[7:0] addr, bit[31:0] data);void'(reg_gen.randomize() with {cmd == `WRITE; addr == local::addr; data == local::data;});reg_gen.start();endtaskvirtual task read_reg(bit[7:0] addr, output bit[31:0] data);void'(reg_gen.randomize() with {cmd == `READ; addr == local::addr;});reg_gen.start();data = reg_gen.data;endtaskendclassclass mcdf_data_consistence_basic_test extends mcdf_base_test;  //填充do_reg(),do_formatter()和do_data()function new(string name = "mcdf_data_consistence_basic_test");super.new(name);endfunctiontask do_reg();bit[31:0] wr_val, rd_val;// slv0 with len=8,  prio=0, en=1wr_val = (1<<3)+(0<<1)+1;this.write_reg(`SLV0_RW_ADDR, wr_val);this.read_reg(`SLV0_RW_ADDR, rd_val);void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));// slv1 with len=16, prio=1, en=1wr_val = (2<<3)+(1<<1)+1;this.write_reg(`SLV1_RW_ADDR, wr_val);this.read_reg(`SLV1_RW_ADDR, rd_val);void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));// slv2 with len=32, prio=2, en=1wr_val = (3<<3)+(2<<1)+1;this.write_reg(`SLV2_RW_ADDR, wr_val);this.read_reg(`SLV2_RW_ADDR, rd_val);void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));// send IDLE commandthis.idle_reg();endtasktask do_formatter();void'(fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});fmt_gen.start();endtasktask do_data();void'(chnl_gens[0].randomize() with {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });void'(chnl_gens[1].randomize() with {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;});void'(chnl_gens[2].randomize() with {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;});forkchnl_gens[0].start();chnl_gens[1].start();chnl_gens[2].start();join#10us; // wait until all data haven been transfered through MCDFendtaskendclassendpackage

划分测试功能点

测试功能点 测试内容 测试通过标准 测试类名
寄存器读写测试 所有控制寄存器的读写测试;所有状态寄存器的读写测试 读写值是否正确 mcdf_reg_write_read_test
寄存器稳定性测试 非法地址读写;对控制寄存器的保留域进行读写;对状态寄存器进行写操作 通过写入和读出,确定寄存器的值时预期值,而不是紊乱值,同时非法寄存器操作也不能影响MCDF的整体功能 mcdf_reg_illegal_access_test
数据通道开关测试 对每一个数据通道对应的控制寄存器域en配置为0,在关闭状态下测试数据写入是否通过 在数据通过关闭情况下,数据无法写入,同时ready信号应该保持为低,表示不接收数据,但又能使得数据不被丢失,因此数据只会停留在数据通道端口 mcdf_channel_disable_test
优先级测试 将不同数据通道配置为相同或者不同的优先级,在数据通道使能的情况下进行测试 如果优先级相同,那么arbiter应该采取轮询机制从各个通道接受数据,如果优先级不同,那么arbiter应该先接收高优先级通道的数据,同时最终所有的数据都应该从MCDF发送出来 mcdf_arbiter_priority_test
发包长度测试 将不同数据通道随机配置为各自的长度,在数据通道使能的情况下进行测试 从formatter发送出来的数据包长度应该同对于通道寄存器的配置值保持一一对应,同时数据也应该保持完整 mcdf_formatter_length_test
下行从端低带宽测试 将MCDF下行数据接收端设置为小存储量,低带宽的类型,由此使得在由formatter发送出数据之后,下行从端有更多的机会延迟grant信号的置位,用来模拟真实场景 在req拉高之后,grant应该在至少两个时钟周期以后拉高,以此来模拟下行从端数据余量不足的情况,当这种激励时序发生10次之后,可以停止测试 mcdf_formatter_grant_test
  • 根据划分的测试点mdcf_checker需实现的一些检查功能
  1. 寄存器读写(参考compare())
  2. 数据通道开关检查:在mcdf_intf中监测en信号,传入mcdf_checker,将3个chnl_intf也传入,task:mcdf_checker::do_channel_disable_check()
    task do_channel_disable_check(int id);forever begin  // 检测硬件信号@(posedge this.mcdf_vif.clk iff (this.mcdf_vif.rstn && this.mcdf_vif.mon_ck.chnl_en[id]===0));if(this.chnl_vifs[id].mon_ck.ch_valid===1 && this.chnl_vifs[id].mon_ck.ch_ready===1)rpt_pkg::rpt_msg("[CHKERR]", $sformatf("ERROR! %0t when channel disabled, ready signal raised when valid high",$time), rpt_pkg::ERROR, rpt_pkg::TOP);endendtask
  1. 优先级检查:观测arbiter信号并定义task:mcdf_checker::do_arbiter_priority_check()
    task do_arbiter_priority_check();int id;forever begin@(posedge this.arb_vif.clk iff (this.arb_vif.rstn && this.arb_vif.mon_ck.f2a_id_req===1));id = this.get_slave_id_with_prio();if(id >= 0) begin@(posedge this.arb_vif.clk);if(this.arb_vif.mon_ck.a2s_acks[id] !== 1)rpt_pkg::rpt_msg("[CHKERR]",$sformatf("ERROR! %0t arbiter received f2a_id_req===1 and channel[%0d] raising request with high priority, but is not granted by arbiter", $time, id),rpt_pkg::ERROR,rpt_pkg::TOP);endendendtaskfunction int get_slave_id_with_prio(); // 模拟arbiter仲裁逻辑int id=-1;int prio=999;foreach(this.arb_vif.mon_ck.slv_prios[i]) beginif(this.arb_vif.mon_ck.slv_prios[i] < prio && this.arb_vif.mon_ck.slv_reqs[i]===1) beginid = i;prio = this.arb_vif.mon_ck.slv_prios[i];endendreturn id;endfunction
  1. 发包长度检查(已实现)
  2. 下行从端低带宽测试:对下行从端配置约束,使其可以实现在低带宽数据消耗下自身缓存量逐渐减少,而频繁在formatter request信号拉高时延迟grant信号拉高;考虑如何观测request与grant之间超过两个时钟周期的时序延迟

仿真命令

vsim -novopt -classdebug -solvefaildebug -sv_seed 0 +TESTNAME=mcdf_data_consistence_basic_test -l mcdf_data_consistence_basic_test.log work.tb
log -r /*  #保存所有波形
run -all

-classdebug:提供更多SV类调试功能
-solvefaildebug:在SV随机化失败后提示更多信息

SV实验3 子系统验证和测试点划分相关推荐

  1. 《计算机网络实验教程》——验证性实验

    文章目录 一.简介 二.验证性实验 ipconfig ping tracert ARP DHCP netstat DNS cache 一.简介 实验教程原网页来自棋歌教学网 本计算机网络实验教程由验证 ...

  2. 计算机网络实验一:验证性实验

    Chris的计算机网络实验一:验证性实验 Ipconfig Ipconfig 概念 Ipconfig 实作一 Ping Ping 概念 Ping 实作一 Ping 实作二 Tracert Tracer ...

  3. [生命科学] 生物基础实验之PCR验证

    生物基础实验之PCR验证 文章目录 生物基础实验之PCR验证 实验步骤一 实验步骤二 实验步骤三 配胶 实验步骤四 电泳 实验步骤五 跑胶 实验步骤一 在离心管加入7.5μL Master Mix 溶 ...

  4. 计算机仿真实验单摆,计算机仿真实验用凯特摆测重力加速度.doc

    计算机仿真实验 用凯特摆测重力加速度 实验目的 学习凯特摆的实验设计思想和技巧. 掌握一种比较精确的测量重力加速度的方法. 实验内容 仿真仪器调节: 测量和记录: 数据处理: 实验原理 设一质量为m的 ...

  5. 计算机仿真实验单摆,计算机仿真实验 用凯特摆测重力加速度

    实验1 计算机仿真实验 用凯特摆测重力加速度 一. 实验目的 1. 学习凯特摆的实验设计思想和技巧. 2. 掌握一种比较精确的测量重力加速度的方法. 二. 实验内容 1. 仿真仪器调节: 2. 测量和 ...

  6. 在远程FPGA虚拟实验平台上验证七段译码器

    在远程FPGA虚拟实验平台上验证七段译码器 VirtualBoard模块代码 SevenSegDecode模块代码 在远程FPGA实验平台验证七段译码器 第一步:申请实验板 第二步:加载 FPGA 电 ...

  7. 实验二使用交换机2960进行VLAN划分

    实验二  使用S2126G交换机进行VLAN划分 一:实验目的: 通过本次实验,让读者对交换机的配置方法有一个初步的认识,并熟悉交换机配置的各种常用命令.能对虚拟局域网配置是否正确进行测试. 二:实验 ...

  8. 【模电实验】【验证性实验——基本差动放大电路实验】

    实验4-1 验证性实验--基本差动放大电路实验 1. 静态工作点的测试 按照下图连接电路,检查无误后将A, B两端短接,接通电源12 V, 分别测量三极管各极对地的电压值,推算静态电流,记入下表,并与 ...

  9. 【模电实验】【超值1 + 1】【验证性实验——比例、求和运算电路实验】【验证性实验——各种非正弦信号发生器实验】

    实验6-1 验证性实验--比例.求和运算电路实验 1. 反相比例放大电路实验 参照下图连接电路,确认无误后接通电源 其中仿真图如下: 测量静态工作点 根据表 4.6.1 中的参数对电路直流工作点进行测 ...

最新文章

  1. [20170206]为什么少1个段.txt
  2. 注意力机制YYDS,AI编辑人脸终于告别P一处而毁全图
  3. 教程:测试期间的日志记录
  4. 怎样提高WebService性能大数据量网络传输处理(转)
  5. 计算机基础ABCDEF,计算机应用基础_在线作业ABCDEF.docx
  6. oracle中with的用法及用处
  7. select下拉框option的样式修改
  8. 编译C#和C++共存的解决方案的小结
  9. python把字符串转成字典
  10. QT5.9的安装和配置
  11. 第十届南京邮电大学网络攻防大赛(NCTF 2021)writeup
  12. Unity源码分享之 电视遥控器按钮事件控制
  13. 页面访问量统计java_java实现页面访问量统计的实例
  14. 使用iperf3网络速度测试工具测试Windows和Ubuntu两台机器的网口速率
  15. PostgreSQL alter语法
  16. 自定义View - 仿QQ运动步数效果
  17. Kubernetes网络插件flannel和calico
  18. gdiplus 水印_GDI+ 实现透明水印和文字
  19. 标签体系-内容建设思路
  20. windows下MySQL修改root密码

热门文章

  1. 【ava数据集】ava数据集下载 使用迅雷
  2. ibm笔记本修复计算机开机按,IBM X280开机报错,按了Esc后正常解决方法
  3. 计算机网络 - chunk协议
  4. IBM x3750 M4安装ESXi5.5
  5. 服务器创建虚拟环境跑代码
  6. 「GoCN酷Go推荐」高性能内存缓存 ristretto
  7. 计算机电路基础第二版张虹,第1章节电路的基本概念与剖析方法——第1讲.ppt
  8. 漫步数理统计三十——依概率收敛
  9. Visual Studio 2019 下载地址
  10. WPS JS宏示例——工作表排序