基于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 / 1ps`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)B15to0H: 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.mzph.cn/news/750715.shtml

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

相关文章

浏览器如何进行静态资源缓存?—— 强缓存 协商缓存

在平时使用浏览器排查问题的过程中&#xff0c;我们有时会看到浏览器网络请求中出现304状态码&#xff0c;那么是什么情况下出现304呢&#xff1f;下面是关于这一现象的解释&#xff1a; 浏览器如何进行静态资源缓存&#xff1f;—— 强缓存 & 协商缓存 状态码 304浏览器如…

Rust写一个wasm入门并在rspack和vite项目中使用(一)

rust打包wasm文档 文档地址 安装cargo-generate cargo install cargo-generate 安装过程中有问题的话手动安装cargo-generate下载地址 根据自己的系统下载压缩包&#xff0c;然后解压到用户/.cargo/bind目录下&#xff0c;将解压后的文件放到该目录下即可。 创建wasm项目 …

校园闲置物品租售系统|基于springboot框架+ Mysql+Java+B/S架构的校园闲置物品租售系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 ​编辑 用户功能模块 商品购买管理 卖家功能模块 商品…

[密码学]OpenSSL实践篇

背景 最近在写Android abl阶段fastboot工具&#xff0c;需要我在Android代码中实现一些鉴权加解密相关的fastboot命令&#xff0c;里面用到了OpenSSL。我们先来实践一下OpenSSL在Linux系统中的指令。 OpenSSL官方网站&#xff1a;OpenSSL 中文手册 | OpenSSL 中文网 1. 查看…

m3u8,一个超酷的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超酷的 Python 库 - m3u8。 Github地址&#xff1a;https://github.com/globocom/m3u8 在网络视频传输中&#xff0c;HLS&#xff08;HTTP Live Streaming&#xff09;是一…

2.vscode 配置python开发环境

vscode用着习惯了,也不想再装别的ide 1.安装vscode 这一步默认已完成 2.安装插件 搜索插件安装 3.选择调试器 Ctrl Shift P&#xff08;或F1&#xff09;&#xff0c;在打开的输入框中输入 Python: Select Interpreter 搜索&#xff0c;选择 Python 解析器 选择自己安…

科研绘图一:箱线图(添加贝赛尔曲线)

R语言绘图系列—箱线图贝赛尔曲线 &#xff08;一&#xff09;: 科研绘图一&#xff1a;箱线图&#xff08;添加贝赛尔曲线&#xff09; 文章目录 R语言绘图系列---箱线图贝赛尔曲线&#xff08;一&#xff09;: 科研绘图一&#xff1a;箱线图&#xff08;添加贝赛尔曲线&…

plt保存PDF矢量文件中嵌入可编辑字体(可illustrator编辑)

背景&#xff1a; 用默认 plt.savefig() 保存图片&#xff0c;图中文字是以瞄点保存&#xff0c;而不是以文字格式。在编辑矢量图中&#xff0c;无法调整文字大小和字体。 方法&#xff1a; import matplotlib.pyplot as plt import numpy as np# ------输出的图片为illustr…

基于Springboot和Redis实现的在线选课系统

1.项目简介 1.1 介绍 毕业设计真的就是demo吗&#xff1f;作为工作前的最后一个校园项目&#xff0c;毕业设计应当尽可能的贴近企业实战&#xff0c;业务不必很复杂&#xff0c;但要做到麻雀虽小五脏俱全。本期学长跟大家一起分享如何开发一个在线选课系统&#xff0c;需求也…

2.二进制的方式读写文件

文章目录 写入文件代码运行结果 读出文件代码运行结果 文件打开模式标记&#xff08;查表&#xff09; 写入文件 ------写文件一共五步&#xff1a;------ 第一步&#xff1a;包含头文件 第二步&#xff1a;创建流对象 第三步&#xff1a;指定方式打开文件 第四步&#xff1a;…

通过路由器监控,优化网络效率

路由器是网络的基本连接组件&#xff0c;路由器监控涉及将路由器网络作为一个整体进行管理&#xff0c;其中持续监控路由器的性能、运行状况、安全性和可用性&#xff0c;以确保更好的操作和最短的停机时间&#xff0c;因此监控路由器至关重要。 为什么路由器监控对组织很重要…

Oracle19c静默部署

Oracle19c静默部署文档 下载地址 https://www.oracle.com/database/technologies/oracle-database-software-downloads.html#db_free 一、系统基础配置 1、创建用户和用户组 # 创建oinstall和dba用户组 groupadd oinstall groupadd dba# 创建Oracle用户 useradd -g oinstall…

C语言中,基本数据类型介绍

C语言当中各种数据类型的大小&#xff0c;首先要了解有哪些数据类型。 一 字符型&#xff1a; 整数&#xff08;字符&#xff09;类型存储大小值范围char1 字节-128 到 127 或 0 到 255&#xff08;2的8次方&#xff09;unsigned char1 字节0 到 255&#xff08;&#xff09;s…

前端框架的发展史介绍框架特点

目录 1.前端框架的发展历程 2.官网、优缺点、使用场景 2.1 jQuery 2.2 AngularJS 2.3 React 2.4 Vue.js 2.5 Angular 1.前端框架的发展历程 jQuery&#xff08;2006年&#xff09;&#xff1a;jQuery是一个非常流行的JavaScript库&#xff0c;用于简化DOM操作和事件处理…

【数据库】基础操作

系列文章目录 &#x1f308;座右铭&#x1f308;&#xff1a;人的一生这么长、你凭什么用短短的几年去衡量自己的一生&#xff01; &#x1f495;个人主页:清灵白羽 漾情天殇_计算机底层原理,深度解析C,自顶向下看Java-CSDN博客 ❤️相关文章❤️&#xff1a;清灵白羽 漾情天…

【Qt问题】vs里直接打开qt项目的ui会崩溃

问题描述&#xff1a; 当我用VS2019开发Qt的时候&#xff0c;在VS编译器里要用到Qt的ui设计&#xff0c;但是双击打开发现直接就崩溃了。 解决办法&#xff1a; 崩溃的原因很简单&#xff0c;是因为VS默认用的qt designer打开&#xff0c;所以会崩溃&#xff0c;我们右键单击…

还是了解下吧,大语言模型调研汇总

大语言模型调研汇总 一. Basic Language ModelT5GPT-3LaMDAJurassic-1MT-NLGGopherChinchillaPaLMU-PaLMOPTLLaMABLOOMGLM-130BERNIE 3.0 Titan 二. Instruction-Finetuned Language ModelT0FLANFlan-LMBLOOMZ & mT0GPT-3.5ChatGPTGPT-4AlpacaChatGLMERNIE BotBard 自从Cha…

软考 系统架构设计师之回归及知识点回顾(7)

接前一篇文章&#xff1a;软考 系统架构设计师之回归及知识点回顾&#xff08;6&#xff09; 11. 云计算 背景 大数据和云计算已成为IT领域的两种主流技术。“数据是重要资产”这一概念已成为大家的共识&#xff0c;众多公司争相分析、挖掘大数据背后的重要财富。同时学术界、…

使用 Python 编写程序保护您的眼睛

眼睛&#xff0c;是心灵的窗户&#xff0c;生活在数字时代的我们&#xff0c;眼睛首当其冲地承受冲击。盯着电脑屏幕成为我们日常工作和学习的一部分&#xff0c;导致用眼过度。那如何减少对眼睛的伤害&#xff0c;应该如何保护眼睛&#xff1f; 用眼应控制时间&#xff0c;自…

wait/notify/notifyAll 方法的使用注意事项

wait/notify/notifyAll 方法的使用注意事项 wait/notify/notifyAll 方法的使用注意事项 wait/notify/notifyAll 方法的使用注意事项为什么 wait 必须在 synchronized 保护的同步代码中使用&#xff1f;为什么 wait/notify/notifyAll 被定义在 Object 类中&#xff0c;而 sleep …