前面的内容后面再补,因为近期要C考核了,所以先准备下C考核所需的内容。

RTFSC(2)

整理一条指令在NEMU中的执行结果

从函数中跳转,宏嵌套中慢慢进入最终的代码,人肉gdb一下,

比如在sdb中执行一次si,那么根据cmd_si代码那我们会跳转到cpu_exec()中

static int cmd_si(char *args) {
if (args == NULL) {
cpu_exec(1);
return 0;
} int i = atoi(args);
if (i <= 0) {
printf("Invalid argument '%s'\n", args);
} else {
cpu_exec(i);
} return 0;
}

在cpu_exec()中我们进入execute()函数中。

/* Simulate how the CPU works. */
void cpu_exec(uint64_t n) {
g_print_step = (n < MAX_INST_TO_PRINT);//一次执行太多步就不打印了,bool类型的gprintstep就赋值为false
//printf("%d\n",nemu_state.state);
switch (nemu_state.state) {
case NEMU_END: case NEMU_ABORT: case NEMU_QUIT:
printf("Program execution has ended. To restart the program, exit NEMU and run again.\n");
return;//如果状态是结束了,出错了,退出了就打印“退出nemu”。
default: nemu_state.state = NEMU_RUNNING;//默认running
} uint64_t timer_start = get_time();//获取执行指令前的时间 execute(n); uint64_t timer_end = get_time();//获取执行指令后的时间
g_timer += timer_end - timer_start; //看执行了多久。 switch (nemu_state.state) {
case NEMU_RUNNING: nemu_state.state = NEMU_STOP; break; case NEMU_END: case NEMU_ABORT:
Log("nemu: %s at pc = " FMT_WORD,
//nemu出错或者异常退出就用红色打印,正常退出就绿色打印。
(nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) :
(nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) :
ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))),
nemu_state.halt_pc);
// fall through
case NEMU_QUIT: statistic();
}
}

在execute()中跳转到exec_once()中

static void execute(uint64_t n) {
Decode s;
initBuffer(&cb); // 初始化环形缓冲区,大小为BUFFER_SIZE
for (;n > 0; n --) {
exec_once(&s, cpu.pc);
g_nr_guest_inst ++;
trace_and_difftest(&s, cpu.pc);
if (nemu_state.state != NEMU_RUNNING) {break;}
IFDEF(CONFIG_DEVICE, device_update());
}/*条件编译宏,如果CONFIG_DEVICE被定义,则调用device_update函数,如果 CONFIG_DEVICE 没有被定义,
这一行什么都不会生成(等价于被注释掉)。*/
printBuffer(&cb);
}

从exec_once()中跳转到isa_exec_once()中。

static void exec_once(Decode *s, vaddr_t pc) {
s->pc = pc;//当前指令地址
s->snpc = pc;//静态下一条指令地址,默认为pc+4 // if(s->pc == 0x80001480){
// printf("找到一场入口地址\n");
// nemu_state.state = NEMU_STOP;
// } isa_exec_once(s);
cpu.pc = s->dnpc;//动态下一条指令,可能跳转或者分支改变
#ifdef CONFIG_ITRACE//如果启用了 CONFIG_ITRACE,会记录指令的详细信息到日志缓冲区 s->logbuf:
char *p = s->logbuf; p += snprintf(p, sizeof(s->logbuf), FMT_WORD ":", s->pc);//FMT_WORD:格式化字符串(如 "0x%08x"),用于输出 PC 地址。
//printf("0x%08x\n",s->pc);
//printf("0x%08x\n",s->snpc);
int ilen = s->snpc - s->pc; //计算指令长度
int i;
uint8_t *inst = (uint8_t *)&s->isa.inst;
// printf("inst = 0x%08x\n",s->isa.inst);
// printf("inst ***= 0x%08x\n", *inst);
#ifdef CONFIG_ISA_x86
for (i = 0; i < ilen; i ++) { //x86是小段,从低地址开始打印
#else
for (i = ilen - 1; i >= 0; i --) {//riscv是大段,从高地址开始打印
#endif
p += snprintf(p, 4, " %02x", inst[i]); //把指令打印出来
}
int ilen_max = MUXDEF(CONFIG_ISA_x86, 8, 4); //不是x86ilenmax就是4
int space_len = ilen_max - ilen; //计算需要填充的空格数
if (space_len < 0) space_len = 0; //
space_len = space_len * 3 + 1;
memset(p, ' ', space_len);
p += space_len; void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte);//反汇编指令
disassemble(p, s->logbuf + sizeof(s->logbuf) - p, //将反汇编指令出来后传到logbuf里面
MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->isa.inst, ilen);
//muxdef,有点像 ?:,
enqueue(&cb, s->logbuf); #endif
}

从isa_exec_once中在跳转到decode_exec()函数中

int isa_exec_once(Decode *s) {
s->isa.inst = inst_fetch(&s->snpc, 4);
//return一个0回去,但是现在并没有使用这个返回值,可以忽略他。
return decode_exec(s);
}

inst_fetch()是在snpc中获取一条长度为4字节的指令赋值给s->inst,然后执行一遍decode_exec()并返回decode_exec()的返回值。

先一次性将decode_exec()代码全部贴出,很长

static int decode_exec(Decode *s) {
int rd = 0;
word_t src1 = 0, src2 = 0, imm = 0;
s->dnpc = s->snpc; #define INSTPAT_INST(s) ((s)->isa.inst)
#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \
decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \
__VA_ARGS__ ; \
} INSTPAT_START();
//INSTPAT(模式字符串, 指令名称, 指令类型, 指令执行操作);
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm);
INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm); INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli , I, R(rd) = src1 >> BITS(imm, 5, 0));
INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli , I, R(rd) = src1 << BITS(imm, 5, 0));
INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai , I, R(rd) = (int32_t)src1 >> BITS(imm , 4 , 0) );
INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu , I, R(rd) = Mr(src1 + imm, 1));
INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi , I, R(rd) = src1 + imm);
INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu , I, R(rd) = (src1 < imm) ? 1 : 0);
INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti , I, R(rd) = ((int32_t)src1 < ((int32_t)imm)) ? 1 : 0);
INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb , I, R(rd) = SEXT(Mr(src1 + imm, 2),8));
INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh , I, R(rd) = SEXT(Mr(src1 + imm, 2),16));
INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu , I, R(rd) = Mr(src1 + imm, 2));
INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw , I, R(rd) = Mr(src1 + imm, 4));
INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi , I, R(rd) = src1 & imm);
INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori , I, R(rd) = src1 ^ imm);
INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori , I, R(rd) = src1 | imm); //CSR寄存器
INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw , I,
if(imm == 0x305){ //mtvec
R(rd) = cpu.mtvec;
cpu.mtvec = src1;
};
if(imm == 0x300){ //mstatus
R(rd) = cpu.mstatus;
cpu.mstatus = src1;
};
if(imm == 0x341){ //mepc
R(rd) = cpu.mepc;
cpu.mepc = src1;
};
if(imm == 0x342){ //mcause
R(rd) = cpu.mcause;
cpu.mcause = src1;
};
); INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall , I, s->dnpc = isa_raise_intr(11,s->pc);etrace());
INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs , I,
if(imm == 0x305){ //mtvec
R(rd) = cpu.mtvec;
cpu.mtvec |= src1;
};
if(imm == 0x300){ //mstatus
R(rd) = cpu.mstatus;
cpu.mstatus |= src1;
};
if(imm == 0x341){ //mepc
R(rd) = cpu.mepc;
cpu.mepc |= src1;
};
if(imm == 0x342){ //mcause
// printf("??????????????????????????*****\n");
R(rd) = cpu.mcause;
cpu.mcause |= src1;
};
); INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw , S, Mw(src1 + imm, 4, src2));
INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh , S, Mw(src1 + imm, 2, src2));
INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb , S, Mw(src1 + imm, 1, src2));
INSTPAT("??????? ????? ????? 011 ????? 01000 11", sd , S, Mw(src1 + imm, 8, src2)); INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, R(rd) = s->pc + 4;
s->dnpc = s->pc + imm;
IFDEF(CONFIG_FTRACE, {
if (rd == 1) {
call_trace(s->pc, s->dnpc);
}})
);
INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr , I, R(rd) = s->pc + 4;
s->dnpc = (src1 + imm) & (~1);
IFDEF(CONFIG_FTRACE,{
if (s->isa.inst == 0x00008067)
ret_trace(s->pc);
else if (rd == 1) {call_trace(s->pc, s->dnpc);}
else if (rd == 0 && imm == 0) {call_trace(s->pc, s->dnpc);}
})
);
INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl , R, R(rd) = src1 >> BITS(src2, 4, 0));
INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add , R, R(rd) = src1 + src2);
INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll , R, R(rd) = src1 << BITS(src2 , 4 , 0));
INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt , R, R(rd) = ((int32_t)src1 < (int32_t)src2) ? 1 : 0);
INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu , R, R(rd) = src1 < src2 ? 1 : 0);
INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor , R, R(rd) = src1 ^ src2);
INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or , R, R(rd) = src1 | src2);
INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and , R, R(rd) = src1 & src2);
INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra , R, R(rd) = (int32_t)src1 >> BITS(src2 , 4 , 0));
INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub , R, R(rd) = src1 - src2);
INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul , R, R(rd) = (unsigned)src1 * (unsigned)src2);
//INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div , R, R(rd) = src1 / src2);
//INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem , R, R(rd) = src1 % src2);
//INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu , R, R(rd) = (unsigned)src1 % (unsigned)src2);
//INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu , R, R(rd) = (unsigned)src1 / (unsigned)src2);
INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh , R, R(rd) = ((int64_t)(int32_t)src1 * (int64_t)(int32_t)src2) >> 32;);
INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu , R, R(rd) = ((int64_t)(int32_t)src1 * (int64_t)(uint32_t)src2) >> 32;);
INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu , R, R(rd) = ((int64_t)(uint32_t)src1 * (int64_t)(uint32_t)src2) >> 32;);
INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div , R, if (src2 == 0) R(rd) = -1;else if ((int32_t)src1 == INT32_MIN && (int32_t)src2 == -1) R(rd) = INT32_MIN;else R(rd) = (int32_t)src1 / (int32_t)src2;);
INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu , R, if (src2 == 0) R(rd) = 0xFFFFFFFF;else R(rd) = (uint32_t)src1 / (uint32_t)src2;);
INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem , R, if (src2 == 0) R(rd) = (int32_t)src1;else if ((int32_t)src1 == INT32_MIN && (int32_t)src2 == -1) R(rd) = 0;else R(rd) = (int32_t)src1 % (int32_t)src2;);
INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu , R, if (src2 == 0) R(rd) = (uint32_t)src1;else R(rd) = (uint32_t)src1 % (uint32_t)src2;);
INSTPAT("0011000 00010 00000 000 0000 011100 11", mret , R, s->dnpc = cpu.mepc);
//div注释:
//匹配 div 指令(有符号除法)。
//如果除数 src2 为 0,结果规定为 -1。
//如果被除数是最小负数(INT32_MIN),除数为 -1,结果规定为 INT32_MIN(防止溢出)。
//否则正常做有符号除法。 //printf("mulh:%lx\n", (int64_t)(~src1+1) * (int64_t)src2));
//正确的a5:0001 1001 1101 0010 1001 1010 1011 1001
//INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh , R, R(rd) = SEXT(src1 * src2, 32));
//把寄存器 x[rs2]乘到寄存器 x[rs1]上,都视为 2 的补码,将乘积的高位写入 x[rd]。 INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B,
// if(s->pc == 0x800115c0){
// printf("src1 =%d\n",src1);
// printf("src2 =%d\n",src2);
// printf("pc =0x%08x\n",s->pc);
// printf("imm =0x%08x\n",imm);
// printf("dnpc =0x%08x\n",s->dnpc);
// }
// printf("src1 =%d\n",src1);printf("src2 =%d\n",src2);
// printf("pc =0x%08x\n",s->pc);printf("imm =0x%08x\n",imm);
// printf("dnpc =0x%08x\n",s->dnpc);
if(src1 == src2) s->dnpc = s->pc + imm);
INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, if(src1 != src2) s->dnpc = s->pc + imm);
INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, s->dnpc = ((int32_t)src1< (int32_t)src2) ? s->pc + imm : s->dnpc);
INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge , B, s->dnpc = ((int32_t)src1>=(int32_t)src2) ? s->pc + imm : s->dnpc);
INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu , B, s->dnpc = (src1< src2) ? s->pc + imm : s->dnpc);
INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu , B, s->dnpc = (src1>=src2) ? s->pc + imm : s->dnpc); INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0
INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv , N, INV(s->pc));
INSTPAT_END(); R(0) = 0; // reset $zero to 0 return 0;
}

其中INSTPAT()意思是

首先我们来解析一下里面的宏函数

  1. 先来看一下这个INSTPAT_MATCH()定义了一条模式匹配规则

INSTPAT(模式字符串, 指令名称, 指令类型, 指令执行操作);如果匹配上了,那就直接用C语言的goto跳转到INSTPAT_END表示为

#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \
decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \
__VA_ARGS__ ; \
}

其中的s是译码所需的信息比如pc,snpc,dnpc,isa,logbuf(用于ITRACE)

其中的decode_operand

static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) {
uint32_t i = s->isa.inst;
int rs1 = BITS(i, 19, 15);
int rs2 = BITS(i, 24, 20);
*rd = BITS(i, 11, 7);
switch (type) {
case TYPE_I: src1R(); immI(); break;
case TYPE_U: immU(); break;
case TYPE_S: src1R(); src2R(); immS(); break;
case TYPE_J: immJ(); break;
case TYPE_B: src1R(); src2R(); immB(); break;
case TYPE_R: src1R(); src2R(); break; }
}

理解为译码的预处理,BITS()的用法有点像verilog中的变量[1:0]的味道,也就是取这个区间的位宽,在看riscv手册中提到,指令中的源寄存器,目的寄存器,立即数基本上都有对应的位置。如果检测到一条指令是什么类型的便把需要的值赋给rs1,rs2,*rd,imm即可。此外SEXT()用于符号扩展。在匹配过程最后有一条inv的规则,如果前面所有的匹配模式都没有成功那就把此指令视为非法指令

此时取指、译码功能便实现了,接下来就是执行,访存、回写。

INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm);

都在后面的R(rd) = s->pc + imm实现了。

这个操作懂了之后,后面运行更多的程序你就知道怎么做了。

首先程序会被riscv工具链交叉编译之后生成机器码之后将一串数据放到img中,你的nemu就根据img的值不断取指、译码、执行、访存、回写等等。那你此时只需要去看am的cpu-test被汇编成什么指令,比如展示一下dummy.c汇编出来的东西放在了dummy-riscv32-nemu.txt,你就只需看你少了什么指令然后补充上去就可以了。

下一期介绍一下cpu-test一堆例程是如何跑在nemu上运行的,顺便复习一下对AM的理解。

一生一芯学习:pa2.1 RTFM的更多相关文章

  1. Linux下学习FPGA

    声明(叠甲):鄙人水平有限,本文章仅供参考. 1.环境 推荐使用 Ubuntu20.04这是我使用多个版本中最好用的一个,相关安装教程可以自行上网搜索这不再赘述,但要补充的一点的是源推荐使用中科大的源 ...

  2. 动手学TCP——CS144实验感想

    在Stanford CS144的课程实验Lab0~Lab4中,我们动手实现了一个自己的TCP协议,并且能够真的与互联网通信!此外,感谢Stanford开源本实验并提供了大量的优质测试用例,使得我们仅仅 ...

  3. 64位的单周期 RISC-V 模拟器

    分享一个我最近完成过的小项目--64位的单周期 RISC-V 模拟器,这个项目我最近参与一生一芯计划过程中完成的一个小项目. 需要用到的相关知识:Verilog.Verilator.计算机组成原理.汇 ...

  4. 教你21天学会C++ (有图有真相)

    这张图,是在一位有十多年开发经验的资深前辈博客里看到的,觉得很有趣,分享之~ 这位大神的博客是:http://coolshell.cn 理论是可行的,当你刚开始学习C++,到第21天的时候出门千万要小 ...

  5. 详解六大国产CPU处理器

    一个执着于技术的公众号 CPU作为计算机设备的运算和控制核心,负责指令读取.译码与执行,因研发门槛高.生态构建难,被认为是集成电路产业中的"珠穆朗玛峰". 纵观全球,Intel.A ...

  6. 芯航线FPGA学习套件之4*4矩阵键盘模块测试手册

    芯航线FPGA学习套件之4*4矩阵键盘模块测试手册   本手册以简明扼要的方式介绍芯航线FPGA学习套件提供的矩阵键盘模块的测试方法:   连接开发板,如下所示: 2.将矩阵键盘模块与开发板按如下图所 ...

  7. 芯航线FPGA学习套件之多通道串行ADDA(TLV1544,TLC5620)模块测试手册

    芯航线FPGA学习套件之多通道串行ADDA模块测试手册   本手册以简明扼要的方式介绍芯航线FPGA学习套件提供的ADDA模块的测试方法:   连接开发板,如下所示: 2.将ADDA V1.1模块与开 ...

  8. 创芯Xilinx Microblaze 学习系列第一集

    创芯Xilinx Microblaze 学习系列第一集 Xilinx ISE Design Suite 13.2 The MicroBlaze™ embedded processor soft cor ...

  9. SAP成都研究院马洪波:提升学习力,增强竞争力,收获一生乐趣

    马洪波是SAP成都研究院CEC开发团队三大巨头之一.关于他的背景介绍,参考我以前的公众号文章:SAP成都研究院CEC团队三巨头之一:M君的文章预告. 其实早在2007年,互联网上已经有介绍马洪波的文章 ...

  10. 线段树优化DP学习笔记 & JZOJ 孤独一生题解

    在 \(DP\) 的世界里 有一种题需要单调队列优化 \(DP\) 一般在此时,\(f_i\) 和它的决策集合 \(f_j\) 在转移时 \(i\) 不和 \(j\) 粘在一起(即所有的 \(j\) ...

随机推荐

  1. DotNetCore 提示 系统不支持“big5”编码。System does not support 'big5' encoding.

    C# .NET Core 以 Big 5 (大五碼)編碼格式讀取檔案 (ruyut.com) C# .NET Core 以 Big 5 (大五碼)編碼格式讀取檔案 日期: 4月 27, 2022 之前 ...

  2. 微信小程序 物联网解决方案

    "卓岚物联"微信小程序是方便地查看物联网设备当前数据.历史数据.控制设备的简单有效的方法.查看和控制设备的实现可以在五分钟内完成,简单易用."卓岚物联"微信程序 ...

  3. droidrun-APP端UI自动化测试

    前言 在 APP 迭代频繁的当下,UI 自动化测试用例维护一直是效率瓶颈.droidrun 框架通过自然语言驱动 + 视觉大模型的创新模式,实现了安卓 /iOS 双端自动化测试的智能化与低维护成本,为 ...

  4. Luogu P10501 Cutting Game 题解

    P10501 Cutting Game 博弈论经典题目,考虑使用 SG 函数解决. 但是这一题和有向图游戏的定义不同,在有向图游戏不能操作者判负,而这一题中操作出某个状态者判胜.因此,我们需要进行转化 ...

  5. Ansys 圣维南原理

    简介 圣维南原理 分布于弹性体上一小块面积(或体积)内的荷载所引起的物体中的应力,在离 荷载作用区稍远的地方,基本上只同荷载的合力和合力矩有关:荷载的具体分布只 影响荷载作用区附近的应力分布.还有一种 ...

  6. 自动化数据集成的BI工具,为你提供决策洞察力

    传统的商业智能(BI)报表系统采用的是"业务提报表需求,IT进行开发"的模式.决策管理者和业务人员提出用报表等来展示经营管理数据的需求:接着IT响应需求,进行需求沟通.数据处理加工 ...

  7. 进阶篇:3.3.1)DFM铸造-压铸件设计

    本章目的:设计符合压铸工艺的零件,不再犯简单错误,不必再为反复修改模具而烦恼. 1.基础阅读: 进阶篇:2)DFMA的介绍 进阶篇:2.3)DFMA的运用方法(个人方法) 2.压铸的概念 2.1 压铸 ...

  8. Win11系统桌面没有此电脑的问题

    许多电脑基地的用户安装Windows11系统之后,发现自己的电脑上没有此电脑等这个图标,但是我们在日常使用电脑的时候最经常使用打开的就是这个这个此电脑的,那么我们要怎么把它调出来呢?下面技术员小编就带 ...

  9. Latex基本语法简记

    公式插入方式 行内公式可用\(...\)或$...$ 例如$ f(x)=x^2 $,显示为 $ f(x)=x^2 $ 独立公式(单独另起一行,公式会居中),使用$$...$$或\[...\] 例如:$ ...

  10. 易拉罐WiFi收集器

    网络上关于易拉罐收集WiFi信号的说法并不完全无道理. 这里做一个简单的实践. 硬件 手工如图(手工渣) 正常测得信号衰减大约-50dBm. 经过处理后,信号强度衰减在-33dBm. 原理 天线信号发 ...