基于Verilog的简易CPU设计

前言 

        本篇文章将简单讲解CPU之间各部分的功能及接线,并提供Verilog模拟CPU的各个组成部分。该CPU可以完成一些操作,如:加减法,与或,指令跳转等,最后提供testbench用于测试该CPU的工作情况是否符合预期。

CPU简单讲解

CPU及相关硬件的作用:


Random Access Memory(RAM):随机存储寄存器,即内存,包含所有CPU要处理的数据RAM包含一系列的地址,每个地址对应的都是一个二进制数据。CPU一般将会按顺序从RAM中读取数据,但实际上是可以以任意顺序访问RAM。当计算机运行时,它向RAM发送一个地址以获得那个程序。通常,RAM不会有任何操作,除非CPU与RAM连线的读使能为高电平。如果读使能为高电平,电路导通,RAM将返回这个地址的数据给CPU。从此CPU开始处理数据,处理完成后,它会向RAM发送一个地址,并将读使能置高,接着获取下一个数据。如果CPU需要向RAM写入数据,它将输出一个值和一个地址,并将写使能置高,将数据写入RAM对应的地址上。RAM的地址上存储的二进制数可能表示一条指令,一个数字,一个字符,一个地址等等。


Instruction Set(IS):指令集,即CPU可以完成指令种类的集合,比如两个数相加,移动某个数去某个新地址等等。


Control Unit(CU):控制单元,队长,它从RAM中接收指令,通过解析这个指令,变成其他元件可以理解的命令,即向其他元件输出控制信号。


Arithmetic Logic Unit(ALU):算术逻辑单元,是受控制单元控制的一个元件,ALU执行所有的数学运算,例如之前提到的加法指令中的加法。ALU有两个输入口,inputA和inputB。ALU的工作过程如下:CU从RAM收到了指令,告诉ALU该做什么运算,ALU执行运算并输出结果。除此之外,ALU将会对CU输出一个状态标志信号,表明当前状态和下一个时钟需要做的事。当CU与寄存器的写使能为高电平的时候,ALU输出的结果将会被暂时性的存储到寄存器上。那么如何将寄存器上的数据读出?CU实际上也会有个与寄存器的读使能信号连线,当它为高电平时,寄存器输出该数据。寄存器的输出线路会连在CPU总线上,总线是一组连在计算机里多个元件之间的线路。总线上会有更多独立的寄存器(Group Registers),它们同样通过读使能和写使能与CU相连。它们用于存储刚刚那个寄存器放在总线上的数据。这些组寄存器用于在多个操作间存储数字。


Temporary Register:临时寄存器,由于总线上同一时间一般只能有一个数据,而ALU有两个输入端口,这就意味着我们需要先存好一个数据来自于组寄存器中的数据),再与总线上的数据(来自于组寄存器中的数据)进行运算。存放数据提供给ALU的寄存器就叫临时寄存器。需要注意的是,临时寄存器不需要和CU有读写使能的连线,因为它并不会与总线上的其他寄存器产生冲突。


Instruction Register(IR):指令寄存器,用于存放CU从RAM中读到的指令,它也不需要和CU有读写使能的连线,因为它并不会与总线上的其他寄存器产生冲突。它只会将指令输出给CU,基于这个指令,CU会告诉ALU执行什么运算。


Instruction Address Register:指令地址寄存器,在我的Verilog程序里也叫Program Counter(PC),CPU需要它去了解下一个指令在RAM中的何处,在没有“跳转”指令发生的前提下,下一条指令所在的地址通常是当前指令的所在地址+1,但如果发生了跳转,则新地址应为上一条指令在RAM中的地址+offset,offset随不同的跳转指令而各不相同(是一个变化的值)。因为CU并不需要读取PC中的数据,仅需要写入一条指令所在的地址,因此它们之间只需要有写使能连线,而不需要读使能连线。RAM收到PC传递给它的地址后,将会将这个地址上的数据传给IR,再由IR传给CU。

相关图示

 

Verilog代码 

alu_mux.v

module alu_mux (clk, rst, en_in,offset, rd_q,rs_q,alu_in_sel,alu_a,alu_b,en_out
);input [15:0] rd_q, rs_q ;
input clk, rst, en_in, alu_in_sel ;
input [7:0] offset ;
output reg [15:0] alu_a, alu_b ;
output reg en_out ;always @ (negedge rst or posedge clk) beginif (rst == 1'b0) beginalu_a <= 16'b0000_0000_0000_0000 ;alu_b <= 16'b0000_0000_0000_0000 ;en_out  <= 1'b0;end		else if (en_in == 1'b1) beginalu_a <= rd_q;en_out  <= 1'b1;if (alu_in_sel == 1'b0) alu_b <= {{8{offset[7]}}, offset[7:0]} ; else alu_b <= rs_q;					endelse en_out  <= 1'b0;end
endmodule/* alu_mux: 用于给alu输入数据,若alu_in_sel==0,则说明只需要一个操作数若alu_in_sel==1,则需要两个操作数当en_in是能有效时,输出使能有效
*/

alu.v

`timescale 1ns / 1ns`define B15to0H     3'b000
`define AandBH      3'b011
`define AorBH       3'b100
`define AaddBH      3'b001
`define AsubBH      3'b010
`define leftshift   3'b101
`define rightshift  3'b110module alu (clk, rst, en_in, alu_a, alu_b, alu_func, en_out, alu_out
);input  [15:0] alu_a, alu_b ;
input  clk, rst, en_in ;
input  [2:0] alu_func ;
output reg [15:0] alu_out ;
output reg en_out ;always @ (negedge rst or posedge clk) beginif (rst == 1'b0) beginalu_out <= 16'b0000_0000_0000_0000 ;en_out  <= 1'b0 ;end				else beginif (en_in == 1'b1) beginen_out <= 1'b1;case (alu_func)// please add your code hereB15to0H: alu_out <= alu_b ; AandBH: alu_out <= a & b ; AorBH: alu_out <= a | b ; AaddBH: alu_out <= a + b ; AsubBH: alu_out <= a - b ; leftshift: alu_out <= (alu_out << 1) ; rightshift: alu_out <= (alu_out >> 1) ;default: alu_out <= alu_out ; endcaseendelse en_out <= 1'b0;endend
endmodule/*根据输入alu_func的值,确定如何将两个操作数alu_a和alu_b运算得到alu_out
*/

control_unit.v

module control_unit (clk,rst,en,en_alu,en_ram_out,ins,offset_addr,en_ram_in,	en_group_pulse,en_pc_pulse,reg_en,alu_in_sel,alu_func,pc_ctrl  		
);/*en: Control_Unit的使能信号en_alu: alu的输出使能信号,即用于告诉control unit此时alu有输出en_ram_out: RAM的输出使能信号,即用于告诉control unit此时RAM有输出ins: 当前的指令
*/
input clk, rst, en, en_alu, en_ram_out ;	
input [15:0] ins ;/*en_ram_in: RAM输入使能,告诉RAM此时有数据输入en_group_pulse: 与datapath同步时钟信号?en_pc_pulse: 连datapath的en_pc_pulsealu_in_sel: 连datapath的alu_in_seloffset_addr: 连datapath的offset_addrreg_en: 连datapath的reg_enalu_func: 连datapath的alu_funcpc_ctrl: 连datapath的pc_ctrl
*/
output en_ram_in, en_group_pulse, en_pc_pulse, alu_in_sel ;	
output reg [7:0] offset_addr ;
output [3:0] reg_en ;
output [2:0] alu_func ;
output [1:0] pc_ctrl ;wire [15:0] ir_out ;
wire en_out ;ir ir1(.clk(clk),.rst(rst),.ins(ins),.en_in(en_ram_out),.en_out(en_out),.ir_out(ir_out));state_transition state_transition1(.clk(clk),.rst(rst),.en_in(en),.en1(en_out),.en2(en_alu),.rd(ir_out[11:10]),.opcode(ir_out[15:12]),.en_fetch_pulse(en_ram_in),	.en_group_pulse(en_group_pulse),.en_pc_pulse(en_pc_pulse),.pc_ctrl(pc_ctrl),.reg_en(reg_en),.alu_in_sel(alu_in_sel),.alu_func(alu_func)			);always @ (en_out or ir_out) beginoffset_addr = ir_out[7:0] ;endendmodule

cpu.v

module cpu(clk,rst,en_in,en_ram_out,addr,ins,en_ram_in 	
);input         clk, rst ,en_in, en_ram_out ;
input  [15:0] ins ;
output [15:0] addr ;
output        en_ram_in ;wire         en_pc_pulse, en_group_pulse, alu_in_sel, en_alu ;
wire  [1:0]  pc_ctrl ;
wire  [3:0]  reg_en ;
wire  [2:0]  alu_func ;
wire  [7:0]  offset_addr ;data_path data_path1(.clk(clk),.rst(rst),.offset(ins[7:0]),.offset_addr(offset_addr),.en_pc_pulse(en_pc_pulse),.pc_ctrl(pc_ctrl),.en_in(en_group_pulse),.reg_en(reg_en),.rd(ins[11:10]),.rs(ins[9:8]),.alu_in_sel(alu_in_sel),.alu_func(alu_func),.en_out(en_alu),.pc_out(addr)
);	                     control_unit control_unit1(.clk(clk),.rst(rst),.en(en_in),.en_alu(en_alu),  .en_ram_out(en_ram_out),.ins(ins),.offset_addr(offset_addr),.en_ram_in(en_ram_in),.en_group_pulse(en_group_pulse),.en_pc_pulse(en_pc_pulse),.reg_en(reg_en),.alu_in_sel(alu_in_sel),.alu_func (alu_func),.pc_ctrl(pc_ctrl)			
);	
endmodule				

data_path.v

module data_path (clk, rst,offset_addr,	en_pc_pulse,pc_ctrl, offset, en_in,reg_en,	alu_in_sel,alu_func, en_out,	pc_out,rd,rs		
);input clk, rst, en_pc_pulse, en_in, alu_in_sel ;
input [7:0] offset_addr, offset ;
input [1:0] pc_ctrl, rd, rs ;
input [3:0] reg_en ;
input [2:0] alu_func ;
output en_out ;
output [15:0] pc_out ;wire [15:0] rd_q, rs_q, alu_a, alu_b, alu_out ;	
wire en_out_group, en_out_alu_mux ;  pc pc1(clk(clk),rst(rst),       en_in(en_pc_pulse),pc_ctrl(pc_ctrl),offset_addr(offset_addr), 		 			 pc_out(pc_out)	
);reg_group reg_group1(.clk(clk),.rst(rst),.en_in(en_in),.reg_en(reg_en),.d_in(alu_out),.rd(rd),.rs(rs),.rd_q(rd_q),.en_out(en_out_group),.rs_q(rs_q)		
);alu_mux alu_mux1(                                        .clk(clk),.rst(rst),.en_in(en_out_group),.rd_q(rd_q),.rs_q(rs_q),.offset(offset),.alu_in_sel(alu_in_sel),.alu_a(alu_a),.en_out(en_out_alu_mux),					.alu_b(alu_b)  		
);alu alu1(.clk(clk),.rst(rst),.en_in(en_out_alu_mux),					.alu_a(alu_a),.alu_b(alu_b),.alu_func(alu_func),.en_out(en_out),.alu_out(alu_out ) 
);							
endmodule				/*datapath包含alu,alu_mux等模块
*/

 ir.v

module ir(clk,rst,ins,en_in,en_out,ir_out
);input clk, rst ;
input [15:0] ins ;
input en_in ;
output reg en_out ;
output reg [15:0] ir_out ;always @ (posedge clk or negedge rst) beginif (!rst) beginir_out <= 16'b000000000000 ;en_out <= 1'b1 ;endelse beginif (en_in) beginen_out <= 1'b1 ;ir_out <= ins ;endelse en_out <= 1'b0 ;end
end
endmodule/* IR: Instruction Register, 用于存放当前即将执行的指令在使能有效时,将指令输出给state_transition完成状态转移
*/

pc.v

`timescale 1ns / 1psmodule pc(clk,rst,       en_in,pc_ctrl, // 控制指令寄存器的下一曲值offset_addr, 		 			 pc_out  		
);input clk, rst, en_in ;
input wire [1:0] pc_ctrl ;
input wire [7:0] offset_addr ;
output reg [15:0] pc_out ;always @ (posedge clk or negedge rst) beginif (rst == 0) pc_out <= 0 ;else beginif (en_in == 1) begincase (pc_ctrl) 2'b00: pc_out <= pc_out ; 2'b01: pc_out <= pc_out + 1 ;2'b10: pc_out <= {8'b00000000, offset_addr[7:0]} ; // jump去指定的地址2'b11: pc_out <= pc_out + offset_addr ; default: pc_out <= pc_out ;endcaseendendend   
endmodule/*pc: program counter
*/

reg_group.v

`timescale 1ns / 1psmodule reg_group(clk,       rst,	en_in,		reg_en,		d_in,		rd,rs,		en_out,		rd_q,rs_q	
);
input clk, rst, en_in ; // 时钟信号,复位信号,操作四个寄存器的使能信号
input wire [3:0] reg_en ; // 用于实例化寄存器的使能信号
input wire [15:0] d_in ; // 输入数据
input wire [1:0] rd, rs ; // 用于选择如何分配寄存器的值给两个输出信号,两个输出信号均有4种取值(共有四个寄存器,每个信号读取其中一个寄存器中的值)共16种
output reg en_out ; // 输出使能
output reg [15:0] rd_q, rs_q ; // 输出信号 目标寄存器和源寄存器wire [15:0] q0, q1, q2, q3 ; // 每个寄存器的输出register reg0(.clk(clk),.rst(rst),.en(reg_en[0]),                .d(d_in),                .q(q0)      );register reg1(.clk(clk),.rst(rst),.en(reg_en[1]),                .d(d_in),                .q(q1)      );register reg2(.clk(clk),.rst(rst),.en(reg_en[2]),                .d (d_in),                .q (q2)      );register reg3(.clk(clk),.rst(rst),.en(reg_en[3]),                .d(d_in),                .q(q3)      );      always @ (posedge clk or negedge rst)if (rst == 0) beginrd_q <= 0 ; rs_q <= 0 ;    en_out <= 0; endelse beginif (en_in == 1) beginen_out <= 1 ;   case ({rd[1:0], rs[1:0]})4'b0000: beginrd_q <= q0 ;rs_q <= q0 ;end4'b0001: beginrd_q <= q0 ;rs_q <= q1 ;end   4'b0010: begin rd_q <= q0 ; rs_q <= q2 ; end4'b0011: begin      rd_q <= q0;                                                           rs_q <= q3;end        4'b0100: begin      rd_q <= q1;rs_q <= q0;end        4'b0101: begin      rd_q <= q1;rs_q <= q1;end           4'b0110: begin      rd_q <= q1;rs_q <= q2;end   4'b0111: begin      rd_q <= q1;rs_q <= q3;end                   4'b1000: begin      rd_q <= q2 ;rs_q <= q0 ;end     4'b1001: begin            rd_q <= q2 ;     rs_q <= q1 ;         end       4'b1010: begin       rd_q <= q2 ;rs_q <= q2 ;end                 4'b1011: begin       rd_q <= q2 ;rs_q <= q3 ;end  4'b1100: begin       rd_q <= q3 ;rs_q <= q0 ;end               4'b1101: begin         rd_q <= q3 ;    rs_q <= q1 ;          end       4'b1110: begin       rd_q <= q3 ;rs_q <= q2 ;end         4'b1111: begin       rd_q <= q3 ;rs_q <= q3 ;end         default: beginrd_q <= 0 ;rs_q <= 0 ;endendcaseendelse en_out <= 0 ;end
endmodule               /* 在每个时钟上升沿,确定如何将哪些寄存器中的值赋给rd_q和rs_q,rd_q和rs_q将用于给alu_a和alu_b赋值
*/

register.v

`timescale 1ns / 1psmodule register(clk,rst,en,				d,				q				
);input clk, rst, en ;
input wire [15:0] d ;
output reg [15:0] q ;always @ (posedge clk or negedge rst) beginif (rst == 0) q <= 0 ;else if (en == 1) q <= d ; else q <= q ; end
endmodule/*寄存器模块,在时钟上升沿且Control Unit给出的控制信号en为高时,更新寄存器,否则锁存数据
*/

state_transistion.v

module state_transition(clk,rst,en_in,en1,en2,rd,opcode,en_fetch_pulse,en_group_pulse,en_pc_pulse,pc_ctrl,reg_en,alu_in_sel,alu_func
);input clk, rst ;
input en_in ; // 表示此时有指令需要处理,从空闲状态转为取指状态
input en1 ; // 接收指令寄存器的使能,只有有指令来临时,才会进行状态转移
input en2 ; // 接收alu的输出
input [1:0] rd ; // destination register 目的寄存器
input [3:0] opcode ; // 指令中的操作码,不同的操作码对应不同的next_state/*en_ram_in: RAM输入使能,告诉RAM此时有数据输入en_group_pulse: 与datapath同步时钟信号?en_pc_pulse: 连datapath的en_pc_pulsealu_in_sel: 连datapath的alu_in_selreg_en: 连datapath的reg_enalu_func: 连datapath的alu_funcpc_ctrl: 连datapath的pc_ctrl
*/
output reg en_fetch_pulse ;
output reg en_group_pulse ;
output reg en_pc_pulse ;
output reg [1:0] pc_ctrl;
output reg [3:0] reg_en ;
output reg alu_in_sel ;
output reg [2:0] alu_func ;reg en_fetch_reg, en_fetch ;
reg en_group_reg, en_group ; // group reg的写控制信号和读控制信号
reg en_pc_reg, en_pc ;
reg [3:0] current_state, next_state ; parameter Initial = 4'b0000 ;
parameter Fetch = 4'b0001 ;
parameter Decode = 4'b0010 ;
parameter Execute_Moveb = 4'b0011 ;
parameter Execute_Add = 4'b0100 ;
parameter Execute_Sub = 4'b0101 ;
parameter Execute_And = 4'b0110 ;
parameter Execute_Or = 4'b0111 ;
parameter Execute_Jump = 4'b1000 ;
parameter Write_back = 4'b1001 ;always @ (posedge clk or negedge rst) begin // 有限状态机的现态与次态的转移if (!rst)current_state <= Initial ;else current_state <= next_state ;
endalways @ (current_state or en_in or en1 or en2 or opcode) begincase (current_state)Initial: beginif (en_in)next_state = Fetch ;elsenext_state = Initial ;endFetch: beginif (en1) next_state = Decode ;elsenext_state = current_state ;endDecode: begincase (opcode) 4'b0000: next_state = Execute_Moveb ;4'b0010: next_state = Execute_Add ;4'b0101: next_state = Execute_Sub ;4'b0111: next_state = Execute_And ;4'b1001: next_state = Execute_Or ;4'b1010: next_state = Execute_Jump ;default: next_state = current_state ;endcaseendExecute_Moveb: beginif (en2) // 如果此时alu确实有数输出,证明这个状态完成了next_state = Write_back ;elsenext_state = current_state ;endExecute_Add: beginif(en2) // 如果此时alu确实有数输出,证明这个状态完成了next_state = Write_back ;elsenext_state = current_state ;endExecute_Sub: beginif(en2) // 如果此时alu确实有数输出,证明这个状态完成了next_state = Write_back ;elsenext_state = current_state ;endExecute_And: beginif(en2) // 如果此时alu确实有数输出,证明这个状态完成了next_state = Write_back ;elsenext_state = current_state ;end   Execute_Or: beginif(en2) // 如果此时alu确实有数输出,证明这个状态完成了next_state = Write_back ;elsenext_state = current_state ;end  Execute_Jump: next_state = Fetch ;Write_back: next_state = Fetch ;default: next_state = current_state ;endcase
end// 用于输出控制信号
always @ (rst or next_state) beginif (!rst) beginen_fetch = 1'b0 ;en_group = 1'b0 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b000 ;endelse begincase (next_state)Initial: beginen_fetch = 1'b0 ;en_group = 1'b0 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b000 ;endFetch: beginen_fetch = 1'b1 ;  // 此时需要取指en_group = 1'b0 ;en_pc = 1'b1 ;pc_ctrl = 2'b01 ; // 取下一个指令reg_en = 4'b0000 ; alu_in_sel = 1'b0 ;alu_func = 3'b000 ;endDecode: beginen_fetch = 1'b0 ;en_group = 1'b0 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b000 ;endExecute_Moveb: beginen_fetch = 1'b0 ;en_group = 1'b1 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b000 ;endExecute_Add: beginen_fetch = 1'b0 ;en_group = 1'b1 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b001 ;endExecute_Sub: begin// please add your code hereen_fetch = 1'b0 ;en_group = 1'b1 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b001 ;endExecute_And: begin// please add your code hereen_fetch = 1'b0 ;en_group = 1'b1 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b001 ;endExecute_Or: begin// please add your code hereen_fetch = 1'b0 ;en_group = 1'b1 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b001 ;endExecute_Jump: begin// please add your code hereen_fetch = 1'b0 ;en_group = 1'b1 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b001 ;endWrite_back: begincase (rd)2'b00: reg_en = 4'b0001 ;2'b01: reg_en = 4'b0010 ;2'b10: reg_en = 4'b0100 ;2'b11: reg_en = 4'b1000 ;default: reg_en = 4'b0000 ;endcaseenddefault: beginen_fetch = 1'b0 ;en_group = 1'b0 ;en_pc = 1'b0 ;pc_ctrl = 2'b00 ;reg_en = 4'b0000 ;alu_in_sel = 1'b0 ;alu_func = 3'b000 ;endendcaseend
endalways @ (posedge clk or negedge rst) beginif (!rst) beginen_fetch_reg <= 1'b0 ;en_pc_reg <= 1'b0 ;en_group_reg <= 1'b0 ;endelse beginen_fetch_reg <= en_fetch ; en_pc_reg <= en_pc ;en_group_reg <= en_group ;end
endalways @ (en_fetch or en_fetch_reg)en_fetch_pulse = en_fetch & (~en_fetch_reg) ;always @ (en_pc_reg or en_pc)en_pc_pulse = en_pc & (~en_pc_reg) ; // 此时需要读入下一个指令,且当此时指令寄存器中为空时,请求下一条指令always @ (en_group_reg or en_group)en_group_pulse = en_group & (~en_group_reg) ;endmodule

Testbench

`timescale 1ns / 1psu();
reg         clk,rst,en_in,en_ram_out;
reg  [15:0] ins;
wire        en_ram_in;
wire [15:0] addr;cpu test_cpu(.clk (clk),.rst (rst),.en_in (en_in),.en_ram_in (en_ram_in), .ins (ins),	.en_ram_out (en_ram_out),.addr (addr)   	
);parameter Tclk = 10;initial begin//define clkendinitial begin//define rst endinitial begin                //define en_in and en_ram_out
endinitial begin//define ins ,you can assign 0000_0000_0000_0001//0000_0100_0000_0010 and so on to ins.endinitial begin#(Tclk*400)  $stop;
endendmodule

如有疑问,欢迎打扰。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/536430.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

耳机壳UV树脂制作私模定制耳塞适合什么样的人使用呢?

耳机壳UV树脂制作私模定制耳塞适合什么样的人使用呢&#xff1f; 耳机壳UV树脂制作私模定制耳塞适合以下人群使用&#xff1a; 对音质要求高的人&#xff1a;私模定制耳塞能够完美契合用户的耳朵形状&#xff0c;减少漏音和外部噪音的干扰&#xff0c;提供更好的音质体验。需要…

[媒体宣传]上海有哪些可以邀约的新闻媒体资源汇总

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 上海作为中国最大的城市之一&#xff0c;拥有丰富的新闻媒体资源。以下是一些可以邀约的新闻媒体资源汇总&#xff1a; 报纸媒体&#xff1a; 《新民晚报》&#xff1a;上海最具影响力…

16、技巧之九: 修改参数,如何让表格翻页滚动到底部?【Selenium+Python3网页自动化总结】

1、问题提出 在网页配置参数时&#xff0c;输入参数名称搜索&#xff0c;搜出来的同名参数结果有多个&#xff0c;分布在一个表格的不同行&#xff0c;表格是动态加载的&#xff0c;需要滚动鼠标才能把所出参数找出来。用selenium怎么实现这种参数修改&#xff1f; 2、网页元素…

【深度学习实践】HaGRID,YOLOv5,手势识别项目,目标检测实践项目

文章目录 数据集介绍下载数据集将数据集转换为yolo绘制几张图片看看数据样子思考类别是否转换下载yolov5修改数据集样式以符合yolov5创建 dataset.yaml训练参数开始训练训练分析推理模型转换onnx重训一个yolov5s后记 数据集介绍 https://github.com/hukenovs/hagrid HaGRID&a…

开源办公系统CRM管理系统

基于ThinkPHP6 Layui MySQL的企业办公系统。集成系统设置、人事管理、消息管理、审批管理、日常办公、客户管理、合同管理、项目管理、财务管理、电销接口集成、在线签章等模块。系统简约&#xff0c;易于功能扩展&#xff0c;方便二次开发。 服务器运行环境要求 PHP > 7.…

Kubernetes kafka系列 | k8s部署kafka+zookeepe集群(可外部通信)| kafka docekr镜像制作-v3.5.2

一、 Kafka、ZooKeeper 的分布式消息队列系统总体架构 典型的 Kafka 体系架构包括若干 Producer(消息生产者),若干 Broker(作为 Kafka 节点的服务器),若干 Consumer (Group),以及一个 ZooKeeper 集群。 Kafka 通过 ZooKeeper 管理集群配置、选举 Leader,并在 Consum…

单调队列 维护区间最值(板子+两道练手)

1.P1886 滑动窗口 /【模板】单调队列https://www.luogu.com.cn/problem/P1886 板子题&#xff0c;传送门在上方 // Problem: // P1886 滑动窗口 /【模板】单调队列 // // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P1886 // Memory Limit: 500 MB //…

订单超时自动取消订单实现策略

订单超时&#xff0c;自动取消&#xff0c;在生活中很常见。比如整点秒杀时&#xff0c;下单后&#xff0c;三十分钟没有付款。这个订单就会被自动取消。这个操作的实现策略有下面这几个&#xff1a; 方案一&#xff1a;使用JDK自带的延迟队列 JDK中提供了一种延迟队列数据结构…

【PyTorch][chapter 22][李宏毅深度学习]【无监督学习][ WGAN]【理论二】

前言&#xff1a; 本篇主要参考《Wasserstein GAN and the Kantorovich-Rubinstein Duality》 重点介绍一下 WGAN 的损失函数 是如何通过 Wasserstein Distance 变换过来的。 分为5步&#xff1a; 我们首先建立Wasserstein Distance 极小值形式&#xff0c; 经过对…

Rabbit MQ详解

写在前面,由于Rabbit MQ涉及的内容较多&#xff0c;赶在春招我个人先按照我认为重要的内容进行一定总结&#xff0c;也算是个学习笔记吧。主要参考官方文档、其他优秀文章、大模型问答。自己边学习边总结。后面有时间我会慢慢把所有内容补全&#xff0c;分享出来也是希望可以给…

【bug mysql】‘mysql‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

【bug mysql】‘mysql’ 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 mysql常规错误&#xff0c;安装好了没有设置环境变量&#xff0c;报错了“mysql 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。” 解决办法&#xff1a; 添加M…

安装、配置MySQL

安装相关软件 MySQL Server、MySQL Workbench MySQL Server&#xff1a;专门用来提供数据存储和服务的软件 MySQL Workbench&#xff1a;可视化的 MySQL 管理工具 官网安装 https://www.mysql.com/ 官网 MySQL :: Download MySQL Installer 安装包路径 在这里选择版本和和对应…