一起学习用Verilog在FPGA上实现CNN----(二)卷积层设计

news2024/10/7 12:17:46

1 打开Vivado工程

Vivado工程文件如图:

在这里插入图片描述

打开Vivado软件,打开工程,如图:

在这里插入图片描述

自动升级到当前版本,如图:

在这里插入图片描述

暂时选择现有开发板的型号,如图:

在这里插入图片描述

出现一条警告性信息,暂时先不管,点击OK:

在这里插入图片描述

可以看到完整的工程文件包含如下图:

在这里插入图片描述

2 卷积层设计

自顶而下分析卷积层的设计过程

2.1 Multi Filter Layer

图为该项目的一个卷积层,其中包含了多个卷积核(Filter),模块的输入为图像矩阵和卷积核设置参数,输出为卷积提取的特征矩阵

在这里插入图片描述
图片来自附带的技术文档《Hardware Documentation》

卷积层的原理图如图所示,其中filters的位宽为2400,image的位宽是16384,该层卷积的输出位宽是75264

  • filters位宽计算:卷积核大小为5x5,卷积核个数为6,数据位宽为float16(16bits),所以5x5x6x16=2400
  • image位宽计算:手写数字图像大小为32x32,数据位宽为float16,所以32x32x16=16384
  • outputConv位宽计算:28x28x6x16=75264,式中28x28表示卷积层输出特征矩阵的长和宽,6表示卷积核的数量,数据位宽是float16
  • 补充卷积输出特征尺寸计算:m=[(n-k+2xp)/s]+1,n表示输入图像或特征矩阵的尺寸,k表示卷积核的尺寸,s表示卷积核滑动的步长(stride),p表示填充(padding)。例如,图像大小为32x32,卷积核大小为5x5,步长为1,m=[(32-5+2x0)/1]+1=28

在这里插入图片描述

2.2 Single Filter Layer

单个卷积核层的设计如图,输入为图像矩阵image和单个卷积核filter,输出卷积核处理的特征矩阵

在这里插入图片描述
图片来自附带的技术文档《Hardware Documentation》

原理图如图所示,filter的位宽为400,image的位宽是16384,输出位宽是12544

  • filters位宽计算:卷积核大小为5x5,卷积核个数为1,数据位宽为float16,所以5x5x1x16=400
  • image位宽计算:手写数字图像大小为32x32,数据位宽为float16,所以32x32x16=16384
  • outputConv位宽计算:28x28x1x16=12544,式中28x28表示卷积层输出特征矩阵的长和宽,1表示卷积核的数量,数据位宽是float16

在这里插入图片描述

2.3 Convolution Unit

卷积单元如图所示,输入为卷积核filter和卷积核窗口覆盖的图像image,计算输出该窗口提取的特征

在这里插入图片描述
图片来自附带的技术文档《Hardware Documentation》

原理图如图所示,filter的位宽为400,卷积核窗口覆盖的图像image的位宽是400,输出位宽是16

  • filters位宽计算:卷积核大小为5x5,卷积核个数为1,数据位宽为float16,所以5x5x1x16=400
  • image位宽计算:手写数字图像大小为32x32,卷积核窗口覆盖的图像大小为5x5,数据位宽为float16,所,5x5x16=400
  • result位宽计算:输出结果为float16数据类型的数,具体计算见 2.4 Processing Element 章节

在这里插入图片描述

2.4 Processing Element

卷积单元具体实现如图所示,即相乘相加操作。卷积计算具体操作就是点乘,本质就是乘法和加法。图中输入为float16类型数据A和B,输出float16数据类型的结果
在这里插入图片描述
图片来自附带的技术文档《Hardware Documentation》

原理图如图所示,可以看到输入floatA和floatB,以及输出result位宽均为16

在这里插入图片描述

3 模块功能解析

自底向上分析每个模块的功能和具体实现

3.1 Processing Element

如图所示Processing Element由FM(floatMult16),FADD(floatAdd16),result_reg三个单元组成

  • FM(floatMult16)单元是执行两个float16数据的乘法
  • FADD(floatAdd16)单元是执行两个float16数据的加法
  • result_reg寄存器,存放的是新的求和,将电路从组合逻辑转为同步时序电路,保证数据的同步
    在这里插入图片描述

3.2 Convolution Unit

卷积单元完整的顶层原理图如图所示,对一个卷积核和该卷积核覆盖的图像区域(可以称为窗口)进行计算,输出一个计算结果(float16)

在这里插入图片描述

3.3 Single Filter Layer

Single Filter Layer原理图如图所示,由1个RF selector和14个CU组成,该部分是计算一个卷积核与一幅图像的卷积,输出卷积提取的完整图像的特征。

RF selector的作用:将卷积核覆盖的图像区域(可以称为窗口)的数据对应传输给14个CU,输入图像尺寸为32x32x16,卷积核大小为5x5x16,卷积核滑动步长为1,此时一幅完整图像将产生28x28个窗口数据,每个窗口数据为5x5x16。因为14个CU是并行计算的,故RF selector输出位宽为14x5x5x16=5600

为什么选择使用14个CU,作者给出的解释是:LUT的数量在单个或多个卷积核模块中呈指数增长,实验对比后,最终决定使用CU的数量等于输出特征中单行像素数量的一半。例如,输入图像32x32,卷积核5x5,输出特征为28x28,故CU的数量等于28/2=14

在这里插入图片描述

3.4 Multi Filter Layer

Multi Filter Layer原理图如图所示,由2个convLayerSingle组成,即并行度为2。上述内容可知Multi Filter Layer的输入是图像和6个卷积核,因此6个卷积核分为2个一组,循环3次输入到convLayerSingle,即每次执行2个卷积核与图像的卷积

在这里插入图片描述

4 代码实现

4.1 新建工程

新建工程,操作如图所示:

在这里插入图片描述

输入工程名字和工程路径,如图:

在这里插入图片描述

选择创建RTL工程,如图:

在这里插入图片描述

直接点击Next:

在这里插入图片描述

继续点击Next:

在这里插入图片描述

添加芯片型号,操作如图:

在这里插入图片描述

完成创建:

在这里插入图片描述

4.2 floatAdd16

4.2.1 设计输入

创建工程文件,操作如图:

在这里插入图片描述

创建floatAdd16文件:

在这里插入图片描述

创建完成:

在这里插入图片描述

双击打开,输入如下代码:

module floatAdd16 (floatA,floatB,sum);
	
input [15:0] floatA, floatB;  // 输入float16数据A和B
output reg [15:0] sum;   // 输出为float16数据sum

reg sign; // 输出的正负标志位
reg signed [5:0] exponent; //输出数据的指数,有正负故选择有符号数
reg [9:0] mantissa; //输出数据的尾数
reg [4:0] exponentA, exponentB; //输出数据的阶数
reg [10:0] fractionA, fractionB, fraction;	//fraction = {1,mantissa} 暂存位
reg [7:0] shiftAmount;// 移位寄存器,计算加法时配平阶数
reg cout;

always @ (floatA or floatB) begin
	exponentA = floatA[14:10];
	exponentB = floatB[14:10];
	fractionA = {1'b1,floatA[9:0]};
	fractionB = {1'b1,floatB[9:0]}; 
	
	exponent = exponentA;

	if (floatA == 0) begin						//special case (floatA = 0)
		sum = floatB;
	end else if (floatB == 0) begin					//special case (floatB = 0)
		sum = floatA;
	end else if (floatA[14:0] == floatB[14:0] && floatA[15]^floatB[15]==1'b1) begin //A与B互为相反数的情况
		sum=0;
	end else begin
		if (exponentB > exponentA) begin   // 配平阶数,使得A和B在同一阶数
			shiftAmount = exponentB - exponentA;
			fractionA = fractionA >> (shiftAmount);
			exponent = exponentB;
		end else if (exponentA > exponentB) begin 
			shiftAmount = exponentA - exponentB;
			fractionB = fractionB >> (shiftAmount);
			exponent = exponentA;
		end
		if (floatA[15] == floatB[15]) begin			//A与B同符号
			{cout,fraction} = fractionA + fractionB;
			if (cout == 1'b1) begin
				{cout,fraction} = {cout,fraction} >> 1;
				exponent = exponent + 1;
			end
			sign = floatA[15];
		end else begin						//A与B符号不相同
			if (floatA[15] == 1'b1) begin   // A为负数
				{cout,fraction} = fractionB - fractionA;  // B-A 
			end else begin
				{cout,fraction} = fractionA - fractionB;  // A-B
			end
			sign = cout;
			if (cout == 1'b1) begin
				fraction = -fraction;  // 0-负数,求出该数的绝对值
			end else begin
			end
			//对franction进行阶数配平,求出尾数
			if (fraction [10] == 0) begin
				if (fraction[9] == 1'b1) begin
					fraction = fraction << 1;
					exponent = exponent - 1;
				end else if (fraction[8] == 1'b1) begin
					fraction = fraction << 2;
					exponent = exponent - 2;
				end else if (fraction[7] == 1'b1) begin
					fraction = fraction << 3;
					exponent = exponent - 3;
				end else if (fraction[6] == 1'b1) begin
					fraction = fraction << 4;
					exponent = exponent - 4;
				end else if (fraction[5] == 1'b1) begin
					fraction = fraction << 5;
					exponent = exponent - 5;
				end else if (fraction[4] == 1'b1) begin
					fraction = fraction << 6;
					exponent = exponent - 6;
				end else if (fraction[3] == 1'b1) begin
					fraction = fraction << 7;
					exponent = exponent - 7;
				end else if (fraction[2] == 1'b1) begin
					fraction = fraction << 8;
					exponent = exponent - 8;
				end else if (fraction[1] == 1'b1) begin
					fraction = fraction << 9;
					exponent = exponent - 9;
				end else if (fraction[0] == 1'b1) begin
					fraction = fraction << 10;
					exponent = exponent - 10;
				end 
			end
		end
		mantissa = fraction[9:0];
		if(exponent[5]==1'b1) begin //exponent is negative
			sum = 16'b0000000000000000;
		end
		else begin
			sum = {sign,exponent[4:0],mantissa};//组合数据
		end		
	end		
end

endmodule

如图所示:

在这里插入图片描述

4.2.2 分析与综合

对设计进行分析,操作如图:

在这里插入图片描述

分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述

综合完成后,弹出窗口如下,直接关闭:

在这里插入图片描述

4.2.3 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

创建激励文件,输入文件名:

在这里插入图片描述

创建完成:

在这里插入图片描述

双击打开,输入激励代码:

`timescale 100 ns / 10 ps

module tb_floatAdd16();
reg [15:0] floatA;
reg [15:0] floatB;
wire [15:0] sum;

initial begin
	
	// A + B = 16'h3800 = 0.5
	#0
	floatA = 16'h34CD; // 0.3
	floatB = 16'h3266; // 0.2

	// A + B = 34CD
	#10
	floatA = 16'h34CD;
	floatB = 16'h0000; // 0
	#10
	$stop;
end

floatAdd16 FADD
(
	.floatA(floatA),
	.floatB(floatB),
	.sum(sum)
);

endmodule

如图所示:

在这里插入图片描述

开始进行仿真,操作如下:

在这里插入图片描述

仿真操作,如图:

在这里插入图片描述

调整波形,进行观察:

在这里插入图片描述

仿真波形如图:

在这里插入图片描述

关闭仿真:

在这里插入图片描述

点击OK:

在这里插入图片描述

4.3 floatMult16

4.3.1 设计输入

创建floatMult16文件,如图:

在这里插入图片描述

双击打开,输入如下代码:

module floatMult16 (floatA,floatB,product);

input [15:0] floatA, floatB;  // 输入为两个float16数据A和B
output reg [15:0] product;   // 输出为float16数据

reg sign; // 输出的正负标志位
reg signed [5:0] exponent; // 输出数据的指数,有正负故选择有符号数
reg [9:0] mantissa; // 输出数据的小数
reg [10:0] fractionA, fractionB;	//fraction = {1,mantissa} 计算二进制数据最高位 补1
reg [21:0] fraction; // 相乘结果参数


always @ (floatA or floatB) begin
	if (floatA == 0 || floatB == 0) begin  // A或者B为0的情况
		product = 0;
	end else begin
		sign = floatA[15] ^ floatB[15];  // 异或门判断输出的正负
		exponent = floatA[14:10] + floatB[14:10] - 5'd15 + 5'd2; // 由于借位给fractionA和fractionB,需要先补齐两位指数
	
		fractionA = {1'b1,floatA[9:0]}; // 借位给fractionA
		fractionB = {1'b1,floatB[9:0]}; // 借位给fractionB
		fraction = fractionA * fractionB; // 计算二进制乘法
		//  找到第一个不为0的数字并对指数进行匹配处理
		if (fraction[21] == 1'b1) begin
			fraction = fraction << 1;
			exponent = exponent - 1; 
		end else if (fraction[20] == 1'b1) begin
			fraction = fraction << 2;
			exponent = exponent - 2;
		end else if (fraction[19] == 1'b1) begin
			fraction = fraction << 3;
			exponent = exponent - 3;
		end else if (fraction[18] == 1'b1) begin
			fraction = fraction << 4;
			exponent = exponent - 4;
		end else if (fraction[17] == 1'b1) begin
			fraction = fraction << 5;
			exponent = exponent - 5;
		end else if (fraction[16] == 1'b1) begin
			fraction = fraction << 6;
			exponent = exponent - 6;
		end else if (fraction[15] == 1'b1) begin
			fraction = fraction << 7;
			exponent = exponent - 7;
		end else if (fraction[14] == 1'b1) begin
			fraction = fraction << 8;
			exponent = exponent - 8;
		end else if (fraction[13] == 1'b1) begin
			fraction = fraction << 9;
			exponent = exponent - 9;
		end else if (fraction[12] == 1'b0) begin
			fraction = fraction << 10;
			exponent = exponent - 10;
		end 
	
		mantissa = fraction[21:12];
		if(exponent[5]==1'b1) begin //exponent is negative
			product=16'b0000000000000000;
		end
		else begin
			product = {sign,exponent[4:0],mantissa};// 拼接输出数据
		end
	end
end

endmodule

如图所示:

在这里插入图片描述

4.3.2 分析与综合

将floatMult16设置为顶层:

在这里插入图片描述

关闭上次的分析文件:

在这里插入图片描述

对设计进行分析,操作如图:

在这里插入图片描述

分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述

4.3.3 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

双击打开,输入激励代码:

`timescale 100 ns / 10 ps

module tb_floatMult16();
reg [15:0] floatA;
reg [15:0] floatB;
wire [15:0] product;

initial begin
	
	// 4 * 5
	#0
	floatA = 16'b0100010000000000;
	floatB = 16'b0100010100000000;

	// 0.0004125 * 0
	#10
	floatA = 16'b0000111011000010;
	floatB = 16'b0000000000000000;

	#10
	$stop;
end

floatMult16 FM
(
	.floatA(floatA),
	.floatB(floatB),
	.product(product)
);

endmodule

如图所示:

在这里插入图片描述

将tb_floatMult16设置为顶层:

在这里插入图片描述

开始进行仿真,操作如下:

在这里插入图片描述

添加仿真对象,操作如图:

在这里插入图片描述

开始仿真,如图:

在这里插入图片描述

仿真波形,如图:

在这里插入图片描述

4.4 Processing Element

4.4.1 设计输入

创建processingElement16文件,如图:

在这里插入图片描述

双击打开,输入如下代码:

module processingElement16(clk,reset,floatA,floatB,result);

parameter DATA_WIDTH = 16;  // 数据类型float16

input clk, reset;
input [DATA_WIDTH-1:0] floatA, floatB; // 输入float16数据A和B
output reg [DATA_WIDTH-1:0] result;  // 输出float16数据

wire [DATA_WIDTH-1:0] multResult;
wire [DATA_WIDTH-1:0] addResult;

floatMult16 FM (floatA,floatB,multResult); // float16乘法运算
floatAdd16 FADD (multResult,result,addResult);// float16加法运算

always @ (posedge clk or posedge reset) begin
	if (reset == 1'b1) begin
		result = 0;    // 开始时,result赋值为0
	end else begin
		result = addResult;  // 求和结果不断更新为result,即为累加操作,result作为最后的输出
	end
end

endmodule

如图所示:

在这里插入图片描述

4.4.2 分析与综合

关闭上次的分析文件:

在这里插入图片描述

将processingElement16设置为顶层:

在这里插入图片描述

对设计进行分析,操作如图:

在这里插入图片描述

分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述

4.4.3 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

双击打开,输入激励代码:

`timescale 100 ns / 10 ps

module tb_processingElement16();
reg clk,reset;
reg [15:0] floatA, floatB;
wire [15:0] result;

localparam PERIOD = 100;

always
	#(PERIOD/2) clk = ~clk;

initial begin
	#0
	clk = 1'b0;
	reset = 1;
	// A = 2 , B = 3
	floatA = 16'h4000;
	floatB = 16'h4200;

	#PERIOD
	reset = 0;

	#(2*PERIOD)
	$stop;	
end

processingElement16 PE 
(
	.clk(clk),
	.reset(reset),
	.floatA(floatA),
	.floatB(floatB),
	.result(result)
);
endmodule

如图所示:

在这里插入图片描述

将tb_processingElement16设置为顶层:

在这里插入图片描述

开始进行仿真,操作如下:

在这里插入图片描述

开始仿真,如图:

在这里插入图片描述

仿真波形,如图:

在这里插入图片描述

4.5 Convolution Unit

4.5.1 设计输入

创建convUnit文件,如图:

在这里插入图片描述

双击打开,输入如下代码:

module convUnit(clk,reset,image,filter,result);

parameter DATA_WIDTH = 16;  //数据宽度,float16
parameter D = 1; //卷积核深度
parameter F = 5; //卷积核大小

input clk, reset;
input [0:D*F*F*DATA_WIDTH-1] image, filter; //[0:399] image输入
output [0:DATA_WIDTH-1] result;  //[0:15] result输出

reg [DATA_WIDTH-1:0] selectedInput1, selectedInput2;

integer i;


processingElement16 PE
	(
		.clk(clk),
		.reset(reset),
		.floatA(selectedInput1),
		.floatB(selectedInput2),
		.result(result)
	);

// The convolution is calculated in a sequential process to save hardware
// The result of the element wise matrix multiplication is finished after (F*F+2) cycles (2 cycles to reset the processing element and F*F cycles to accumulate the result of the F*F multiplications) 
always @ (posedge clk, posedge reset) begin
	if (reset == 1'b1) begin // reset
		i = 0;
		selectedInput1 = 0;
		selectedInput2 = 0;
	end else if (i > D*F*F-1) begin // if the convolution is finished but we still wait for other blocks to finsih, send zeros to the conv unit (in case of pipelining)
		selectedInput1 = 0;
		selectedInput2 = 0;
	end else begin // send one element of the image part and one element of the filter to be multiplied and accumulated
		selectedInput1 = image[DATA_WIDTH*i+:DATA_WIDTH];
		selectedInput2 = filter[DATA_WIDTH*i+:DATA_WIDTH];
		i = i + 1;
	end
end

endmodule

如图所示:

在这里插入图片描述

4.5.2 分析与综合

将convUnit设置为顶层:

在这里插入图片描述

关闭上次的分析文件:

在这里插入图片描述

对设计进行分析,操作如图:

在这里插入图片描述

分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述

4.5.3 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

双击打开,输入激励代码:

`timescale 100 ns / 10 ps

module tb_convUnit();
reg clk, reset;
reg [1*5*5*16-1:0] image, filter; // we test with a filter whose size is 2*3*3 
wire [15:0] result;

localparam PERIOD = 100;

always
	#(PERIOD/2) clk = ~clk;

initial begin
	#0
	clk = 1'b0;
	reset = 1;
	// We test with an image part and a filter whose values are all 4 
	// The expected result is 400 generated after 25 clock cycles
	image =  400'h4400440044004400440044004400440044004400440044004400440044004400440044004400440044004400440044004400;
	filter = 400'h4400440044004400440044004400440044004400440044004400440044004400440044004400440044004400440044004400;
	
	#PERIOD
	reset = 0;
	
	#(27*PERIOD)
	$displayh(result);
	$stop;
end

convUnit 
#(
	.DATA_WIDTH(16),
	.D(1),
	.F(5)
)
UUT
(
	.clk(clk),
	.reset(reset),
	.image(image),
	.filter(filter),
	.result(result)
);
endmodule

如图所示:

在这里插入图片描述

将tb_convUnit设置为顶层:

在这里插入图片描述

开始进行仿真,操作如下:

在这里插入图片描述

开始仿真,如图:

在这里插入图片描述

仿真波形,如图:

在这里插入图片描述

4.6 Single Filter Layer

4.6.1 设计输入

创建convLayerSingle工程文件,如图:

在这里插入图片描述

双击打开,输入如下代码:

module convLayerSingle(clk,reset,image,filter,outputConv);

parameter DATA_WIDTH = 16;
parameter D = 1; //卷积核的深度
parameter H = 32; //输入图像的高度
parameter W = 32; //输入图像的宽度
parameter F = 5; //卷积核的大小

input clk, reset;
input [0:D*H*W*DATA_WIDTH-1] image;
input [0:D*F*F*DATA_WIDTH-1] filter;
output reg [0:(H-F+1)*(W-F+1)*DATA_WIDTH-1] outputConv; // output of the module

wire [0:((W-F+1)/2)*DATA_WIDTH-1] outputConvUnits; // output of the conv units and input to the row selector

reg internalReset;
wire [0:(((W-F+1)/2)*D*F*F*DATA_WIDTH)-1] receptiveField; // array of the matrices to be sent to conv units


integer counter, outputCounter;
//counter: number of clock cycles need for the conv unit to finsish
//outputCounter: index to map the output of the conv units to the output of the module

reg [5:0] rowNumber, column; 
//rowNumber: determines the row that is calculated by the conv units
//column: determines if we are calculating the first or the second 14 pixels of the output row

RFselector
#(
	.DATA_WIDTH(DATA_WIDTH),
	.D(D),
	.H(H),
	.W(W),
	.F(F)
) RF
(
	.image(image),
	.rowNumber(rowNumber),
	.column(column),
	.receptiveField(receptiveField)
);

genvar n;

generate //generating n convolution units where n is half the number of pixels in one row of the output image
	for (n = 0; n < (H-F+1)/2; n = n + 1) begin 
		convUnit
		#(
			.D(D),
			.F(F)
		) CU
		(
			.clk(clk),
			.reset(internalReset),
			.image(receptiveField[n*D*F*F*DATA_WIDTH+:D*F*F*DATA_WIDTH]),
			.filter(filter),
			.result(outputConvUnits[n*DATA_WIDTH+:DATA_WIDTH])
		);
	end
endgenerate

always @ (posedge clk or posedge reset) begin
	if (reset == 1'b1) begin
		internalReset = 1'b1;
		rowNumber = 0;
		column = 0;
		counter = 0;
		outputCounter = 0;
	end else if (rowNumber < H-F+1) begin
		if (counter == D*F*F+2) begin //The conv unit finishes ater 1*5*5+2 clock cycles
			outputCounter = outputCounter + 1;
			counter = 0;
			internalReset = 1'b1;
			if (column == 0) begin
				column = (H-F+1)/2;
			end else begin
				rowNumber = rowNumber + 1;
				column = 0;
			end
		end else begin
			internalReset = 0;
			counter = counter + 1;
		end
	end
end

always @ (*) begin
	outputConv[outputCounter*((W-F+1)/2)*DATA_WIDTH+:((W-F+1)/2)*DATA_WIDTH] = outputConvUnits;
end

endmodule

如图所示:

在这里插入图片描述

继续创建RFselector文件:

在这里插入图片描述

双击打开,输入如下代码:

module RFselector(image,rowNumber, column,receptiveField);

parameter DATA_WIDTH = 16;
parameter D = 1; //卷积核深度
parameter H = 32; //图像高度
parameter W = 32; //图像宽度
parameter F = 5; //卷积核尺寸

input [0:D*H*W*DATA_WIDTH-1] image;
input [5:0] rowNumber, column;
output reg [0:(((W-F+1)/2)*D*F*F*DATA_WIDTH)-1] receptiveField;

integer address, c, k, i;

always @ (image or rowNumber or column) begin
	address = 0;
	if (column == 0) begin
		for (c = 0; c < (W-F+1)/2; c = c + 1) begin
			for (k = 0; k < D; k = k + 1) begin
				for (i = 0; i < F; i = i + 1) begin
					receptiveField[address*F*DATA_WIDTH+:F*DATA_WIDTH] = image[rowNumber*W*DATA_WIDTH+c*DATA_WIDTH+k*H*W*DATA_WIDTH+i*W*DATA_WIDTH+:F*DATA_WIDTH];
					address = address + 1;
				end
			end
		end
	end else begin
		for (c = (W-F+1)/2; c < (W-F+1); c = c + 1) begin
			for (k = 0; k < D; k = k + 1) begin
				for (i = 0; i < F; i = i + 1) begin
					receptiveField[address*F*DATA_WIDTH+:F*DATA_WIDTH] = image[rowNumber*W*DATA_WIDTH+c*DATA_WIDTH+k*H*W*DATA_WIDTH+i*W*DATA_WIDTH+:F*DATA_WIDTH];
					address = address + 1;
				end
			end
		end
	end
	
end

endmodule

如图所示:

在这里插入图片描述

4.6.2 分析与综合

将convLayerSingle设置为顶层:

在这里插入图片描述

关闭上次的分析文件:

在这里插入图片描述

对设计进行分析,操作如图:

在这里插入图片描述

分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述

4.6.3 功能仿真

创建TestBench,文件名为操作tb_convLayerSingle,如图所示:

在这里插入图片描述

双击打开,输入激励代码:

`timescale 1ns / 1ps
module tb_convLayerSingle();
reg clk, reset;
reg [1*32*32*16-1:0] image; //We test with a 1*32*32 image
reg [1*5*5*16-1:0] filter; //We test with a 1*5*5 filter
wire [1*28*28*16-1:0] outputConv;

localparam PERIOD = 100;

integer i, clkCounter;

always
	#(PERIOD/2) clk = ~clk;

always @ (posedge clk) begin
	clkCounter = clkCounter + 1;
end

initial begin
	#0
	clkCounter = 0;
	clk = 1'b0;
	reset = 1;
	//We test with the first image and the filters of the first layer of LeNet
	image = 16384'h00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000326638fd3bf038fd32460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032063b773be83be83be83b6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032c73b1f3bf03be83b7f3b4f3be833272606000000000000000000000000000000000000000000000000000000000000000000000000000000000000290533883b073be83bf03be83a5635453be83bf037a8000000000000000000000000000000000000000000000000000000000000000000000000000000000000391d3be83be83be83bf03be83be8360639ee3bf0393d0000000000000000000000000000000000000000000000000000000000000000000000000000000032663b773bf03bf039f637273bf03b2731e634f53c003945000000000000000000000000000000000000000000000000000000000000000000000000000032063b773be83be8399e2a0634b537982d45000000003bf03ba032460000000000000000000000000000000000000000000000000000000000000000000030c5392d3bf03b4f3a8735450000000000000000000000003bf03be8392d0000000000000000000000000000000000000000000000000000000000000000270739963be83b8834742cc52f070000000000000000000000003bf03be83a1e000000000000000000000000000000000000000000000000000000000000000033273be83be833e80000000000000000000000000000000000003bf03be83a1e00000000000000000000000000000000000000000000000000000000000000003a363bf039f600000000000000000000000000000000000000003c003bf03a2600000000000000000000000000000000000000000000000000000000000034c53bb83be8370700000000000000000000000000000000000000003bf03be838a500000000000000000000000000000000000000000000000000000000000035553be83b372e46000000000000000000000000000000002707383c3bf039d62a0600000000000000000000000000000000000000000000000000000000000035553be83aff000000000000000000000000000000002707381c3be83b0f3474000000000000000000000000000000000000000000000000000000000000000035553be8388d00000000000000000000000000003206392d3be8396d00000000000000000000000000000000000000000000000000000000000000000000000035653bf03b0f00000000000000000000000037273b773bf03915000000000000000000000000000000000000000000000000000000000000000000000000000035553be83bd0389532062f47355539963b0f3bf03aff393d3307000000000000000000000000000000000000000000000000000000000000000000000000000035553be83be83be83b2f3abf3be83be83be83a2638140000000000000000000000000000000000000000000000000000000000000000000000000000000000002f073a3e3be83be83bf03be83be83b4f388d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e4638043be83bf03be8386c30a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
	filter = 400'h346b33f83146351432de310e2cc624deb409b3a2b61ab4c8b679b63bb455b48d2b45b08b2bdbb4c0b536b4b9b598b810b521;
	#PERIOD
	reset = 0;

	#((56*28+1)*PERIOD)
	for (i = 28*28-1; i >=0; i = i - 1) begin
		$displayh(outputConv[i*16+:16]);
	end 
	$stop;
end

convLayerSingle UUT
(
	.clk(clk),
	.reset(reset),
	.image(image),
	.filter(filter),
	.outputConv(outputConv)
);
endmodule

如图所示:

在这里插入图片描述

将tb_convLayerSingle设置为顶层:

在这里插入图片描述

开始进行仿真,操作如下:

在这里插入图片描述

开始仿真,如图:

在这里插入图片描述

仿真波形如图所示:

在这里插入图片描述

4.7 Multi Filter Layer

4.7.1 设计输入

创建convLayerMulti文件,如图:

在这里插入图片描述

双击打开,输入如下代码:

module convLayerMulti(clk,reset,image,filters,outputConv);

parameter DATA_WIDTH = 16;
parameter D = 1; //输入图像深度
parameter H = 32; //输入图像高度
parameter W = 32; //输入图像宽度
parameter F = 5; //卷积核尺寸
parameter K = 6; //卷积核数量

input clk, reset;
input [0:D*H*W*DATA_WIDTH-1] image;
input [0:K*D*F*F*DATA_WIDTH-1] filters;
output reg [0:K*(H-F+1)*(W-F+1)*DATA_WIDTH-1] outputConv;

reg [0:2*D*F*F*DATA_WIDTH-1] inputFilters;
wire [0:2*(H-F+1)*(W-F+1)*DATA_WIDTH-1] outputSingleLayers;

reg internalReset;

integer filterSet, counter, outputCounter;

genvar i;

generate
	for (i = 0; i < 2; i = i + 1) begin 
		convLayerSingle #(
		  .DATA_WIDTH(DATA_WIDTH),
		  .D(D),
		  .H(H),
		  .W(W),
		  .F(F)
		) UUT 
		(
			.clk(clk),
	     		.reset(internalReset),
	     		.image(image),
	    		.filter(inputFilters[i*D*F*F*DATA_WIDTH+:D*F*F*DATA_WIDTH]),
	     		.outputConv(outputSingleLayers[i*(H-F+1)*(W-F+1)*DATA_WIDTH+:(H-F+1)*(W-F+1)*DATA_WIDTH])
      		);
  	end
endgenerate

always @ (posedge clk or posedge reset) begin
	if (reset == 1'b1) begin
		internalReset = 1'b1;
		filterSet = 0;
		counter = 0;
		outputCounter = 0;		
	end else if (filterSet < K/2) begin
		if (counter == ((((H-F+1)*(W-F+1))/((H-F+1)/2))*(D*F*F+3)+1)) begin
			outputCounter = outputCounter + 1;
			counter = 0;
			internalReset = 1'b1;
			filterSet = filterSet + 1;
		end else begin
			internalReset = 0;
			counter = counter + 1;
		end
	end
end

always @ (*) begin
	inputFilters = filters[filterSet*2*D*F*F*DATA_WIDTH+:2*D*F*F*DATA_WIDTH];
	outputConv[outputCounter*2*(H-F+1)*(W-F+1)*DATA_WIDTH+:2*(H-F+1)*(W-F+1)*DATA_WIDTH] = outputSingleLayers;
end

endmodule

如图所示:

在这里插入图片描述

4.7.2 分析与综合

将convLayerMulti设置为顶层:

在这里插入图片描述

关闭上次的分析文件:

在这里插入图片描述
对设计进行分析,操作如图:

在这里插入图片描述
分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述
对设计进行综合,操作如图:

在这里插入图片描述

4.7.3 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

双击打开,输入激励代码:

module tb_convLayerMulti();
reg reset, clk;
reg [1*32*32*16-1:0] image;
reg [6*1*5*5*16-1:0] filters;
wire [6*28*28*16-1:0] outputConv;

localparam PERIOD = 100;

integer i;

always
	#(PERIOD/2) clk = ~clk;
	
	
initial begin 
	#0
	clk = 1'b0;
	reset = 1;
	//We test with a 1*32*32 image and 6 5*5 filters, all the values are 4
	//Expected output 4704 (6*28*28) values equal to 400 (16*25)
	 image = 16384'h00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000326638fd3bf038fd32460000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032063b773be83be83be83b6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032c73b1f3bf03be83b7f3b4f3be833272606000000000000000000000000000000000000000000000000000000000000000000000000000000000000290533883b073be83bf03be83a5635453be83bf037a8000000000000000000000000000000000000000000000000000000000000000000000000000000000000391d3be83be83be83bf03be83be8360639ee3bf0393d0000000000000000000000000000000000000000000000000000000000000000000000000000000032663b773bf03bf039f637273bf03b2731e634f53c003945000000000000000000000000000000000000000000000000000000000000000000000000000032063b773be83be8399e2a0634b537982d45000000003bf03ba032460000000000000000000000000000000000000000000000000000000000000000000030c5392d3bf03b4f3a8735450000000000000000000000003bf03be8392d0000000000000000000000000000000000000000000000000000000000000000270739963be83b8834742cc52f070000000000000000000000003bf03be83a1e000000000000000000000000000000000000000000000000000000000000000033273be83be833e80000000000000000000000000000000000003bf03be83a1e00000000000000000000000000000000000000000000000000000000000000003a363bf039f600000000000000000000000000000000000000003c003bf03a2600000000000000000000000000000000000000000000000000000000000034c53bb83be8370700000000000000000000000000000000000000003bf03be838a500000000000000000000000000000000000000000000000000000000000035553be83b372e46000000000000000000000000000000002707383c3bf039d62a0600000000000000000000000000000000000000000000000000000000000035553be83aff000000000000000000000000000000002707381c3be83b0f3474000000000000000000000000000000000000000000000000000000000000000035553be8388d00000000000000000000000000003206392d3be8396d00000000000000000000000000000000000000000000000000000000000000000000000035653bf03b0f00000000000000000000000037273b773bf03915000000000000000000000000000000000000000000000000000000000000000000000000000035553be83bd0389532062f47355539963b0f3bf03aff393d3307000000000000000000000000000000000000000000000000000000000000000000000000000035553be83be83be83b2f3abf3be83be83be83a2638140000000000000000000000000000000000000000000000000000000000000000000000000000000000002f073a3e3be83be83bf03be83be83b4f388d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e4638043be83bf03be8386c30a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
	 filters[0*5*5*16+:5*5*16] = 400'h346b33f83146351432de310e2cc624deb409b3a2b61ab4c8b679b63bb455b48d2b45b08b2bdbb4c0b536b4b9b598b810b521;
  	filters[1*5*5*16+:5*5*16] = 400'h2beb2d7b319a3303349830989e6132afa8af343b345632da34043406345c30ebac9dacf7b0ec2464b26d2e3bb2c4b33cb203;
	 filters[2*5*5*16+:5*5*16] = 400'ha610312fad4522feac2330d832a4319ba5ecaac229349a10afb12f4aaeb8aadb2f99b26021bdac24a968aef7321c29c82d35;
	 filters[3*5*5*16+:5*5*16] = 400'h240634542fc9375033bf3851365635c4a3bd2b162aac2a602c7e31812d6a35d03782310c37c130e932e22624a6b8ab7da1f3;
	 filters[4*5*5*16+:5*5*16] = 400'ha99baabc2aa33113af6bb1db23c8aa0ab69ab575b6ebb60e16d4b1dfac5a31be2f9c2b2ab298b1b6b2cdae2db5c6b4f0af69;
	 filters[5*5*5*16+:5*5*16] = 400'h37fe37f0380b340434572f01309f31f32e76a6dd2aba9fa734cf303536562c91338e34322f47b1442217a6c2a8eba2a8addc;
	#(PERIOD)

	reset = 0;

	
	#(7*1457*PERIOD)
	for (i = 6*28*28-1; i >=0; i = i - 1) begin
		$displayh(outputConv[i*16+:16]);
	end
	$stop;
end

convLayerMulti UUT 
(
	.clk(clk),
	.reset(reset),
	.image(image),
	.filters(filters),
	.outputConv(outputConv)
);

endmodule

如图所示:

在这里插入图片描述
开始进行仿真,操作如下:

在这里插入图片描述
开始仿真,如图:

在这里插入图片描述

4.8 IntegrationConv

4.8.1 设计输入

创建integrationConv文件,如图:

在这里插入图片描述
双击打开,输入如下代码:

module integrationConv (clk,reset,CNNinput,Conv,ConvOutput);

parameter DATA_WIDTH = 16;
parameter ImgInW = 32;
parameter ImgInH = 32;
parameter ConvOut = 28;
parameter Kernel = 5;
parameter DepthC = 6;


input clk, reset;
input [ImgInW*ImgInH*DATA_WIDTH-1:0] CNNinput;
input [Kernel*Kernel*DepthC*DATA_WIDTH-1:0] Conv;
output [ConvOut*ConvOut*DepthC*DATA_WIDTH-1:0] ConvOutput;

convLayerMulti C1
(
	.clk(clk),
	.reset(reset),
	.image(CNNinput),
	.filters(Conv),
	.outputConv(ConvOutput)
);

endmodule

如图所示:

在这里插入图片描述

4.8.2 分析与综合

将integrationConv设置为顶层:

在这里插入图片描述
关闭上次的分析文件:

在这里插入图片描述
对设计进行分析,操作如图:

在这里插入图片描述
分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述
纪念一下,“通信行程卡”于2022年12月13日0时,正式下线

在这里插入图片描述

希望本文对大家有帮助,上文若有不妥之处,欢迎指正

分享决定高度,学习拉开差距

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

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

相关文章

2023年第五届人工智能与机器学习国际会议(FAIML 2023)

2023年第五届人工智能与机器学习国际会议(FAIML 2023) 重要信息 会议网址&#xff1a;www.faiml.org 会议时间&#xff1a;2023年4月14-16日 召开地点&#xff1a;中国北京 截稿时间&#xff1a;2023年3月15日 录用通知&#xff1a;投稿后2周内 收录检索&#xff1a;EI,S…

差分进化算法在图像处理中的应用研究(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 本文为Storn和Price制定的著名差分进化计算智能算法的实现。该算法使用Otsu准则作为适应度函数&#xff0c;可用于使用多个阈值…

为什么球的表面积不能用周长积分而体积可以用面积积分?

问题描述&#xff1a; 将面积从最底下一层层叠到最上面可以得到球体积的正确公式 但是将周长从最底下一层层叠到最上面会得到错误结果&#xff0c;错误结果的几何意义是什么&#xff1f;以及是在什么地方积错了&#xff1f; 解答一&#xff1a; 首先&#xff0c; ∫−RR2πR2…

计算机研究生就业方向之去央企(国企)信息化部门

我一直跟学生们说你考计算机的研究生之前一定要想好你想干什么&#xff0c;如果你只是转码&#xff0c;那么你不一定要考研&#xff0c;至少以下几个职位研究生是没有啥优势的&#xff1a; 1&#xff0c;软件测试工程师&#xff08;培训一下就行&#xff09; 2&#xff0c;前…

C语言:星期一问题

题目需求 整个20世纪&#xff08;1901年1月1日至2000年12月31日之间&#xff09;&#xff0c;一共有多少个星期一&#xff1f; (不要告诉我你不知道今天是星期几哈) 请用一段程序实现了这一功能。 算法思路 判断1901年1月1日到2000年12月31的每一天是星期几&#xff0c;如果是星…

前端开发:JS中关于正则表达式的使用汇总

前言 在前端开发过程中&#xff0c;关于正则表达式的使用也是必备技能&#xff0c;尤其是在实际业务需求的时候&#xff0c;需要处理一些不能按照正常语句操作的逻辑&#xff0c;如前端开发中的字符匹配、参数处理等都需要正则表达式来匹配截取处理。虽然正则表达式在程序开发中…

01-18-spark-入门简介-部署入门

01-spark-入门简介&#xff1a; Spark 是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。 一次性数据计算&#xff1a;框架在处理数据的时候&#xff0c;从存储设备中读取数据&#xff0c;进行逻辑操作&#xff0c;然后将结果存储到介质中。 Hadoop 的 MR 框架和 S…

直播技术分享:千万级直播系统后端架构设计的方方面面

1、引言 本文以TFBOYS“日光旅行”七周年这场直播演唱会为案例&#xff0c;为你分享大型直播系统后端架构设计的方方面面&#xff0c;包括&#xff1a;基本架构、稳定性保障、安全性障、监控报警、应急预案等技术范畴。 案例中的这次演唱会采用了在线实时互动及演唱会现场的多…

【能效管理】关于某项目配套渗滤液工程电能管理系统的设计和应用

摘要&#xff1a; 介绍老港综合填埋场二期配套渗滤液工程电能管理系统&#xff0c;采用智能电力仪表采集配电现场的各种电参量。系统采用现场就地组网的方式&#xff0c;组网后通过现场总线通讯并远传至后台&#xff0c;通过Acrel-3000电能管理系统实现配电回路用电的实时监控…

服开与编排,老兵新传

前段时间&#xff0c;有同学问&#xff1a;编排与服开是什么关系&#xff1f;现在运营商都建设编排系统&#xff0c;那是不是服务开通以后就退出 OSS 舞台了&#xff1f;为什么会出现编排&#xff1f;这些问题对于行业老兵来说感慨万千&#xff0c;而对于新兵来说就要通过追溯其…

深度学习炼丹-超参数设定和网络训练

前言网络层内在参数 使用 3x3 卷积使用 cbr 组合尝试不同的权重初始化方法 图片尺寸与数据增强batch size 设定 背景知识batch size 定义选择合适大小的 batch size学习率和 batch size 关系 学习率参数设定 背景知识什么是学习率如何设置学习率 优化器选择 优化器定义如何选择…

中国制造构建全球产业链,是关于价值链的创新

经过20天激战&#xff0c;世界杯最终四强全部出炉。 与此同时&#xff0c;绿茵场外的品牌营销大战也即将步入终章。据伦敦数据分析咨询公司GlobalData表示&#xff0c;中国赞助商对卡塔尔世界杯总赞助金额为13.95亿美元&#xff0c;蝉联赞助金额榜单的首位。一时间&#xff0c…

一、Docker简介与安装

1、Docker是什么&#xff1f;为什么会出现Docker 假定您在开发一个购物商城&#xff0c;您使用的是一台笔记本电脑而且您的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件。此外&#xff0c;您的…

中国新闻周刊专访:大数据时代,普通人的信息安全如何保证?

技术是一个放大器 本质应该更好地服务人们的生产生活 近年来&#xff0c;随着大数据运用的日益频繁&#xff0c;技术日益成熟&#xff0c;隐私数据的泄露已到了触目惊心的状态。据不完全统计&#xff0c;2022年以来隐私数据泄露的各类案例多达数百万起&#xff0c;受隐私数据泄…

P4 PyTorch Broadcasting

前言&#xff1a; 维度变换 目录&#xff1a; Broadcasting 流程 broadcasting-able code参考: 课时24 Broadcasting-1_哔哩哔哩_bilibili 一 Broadcasting 流程 分三步&#xff1a; i broadcasting 从最后一个维度开始,进行维度对齐 ii 最前面插入一个维度 iii 最后对…

【测绘程序设计】——地形图图幅号计算

为便于地形图测制、管理和使用,各种比例尺地形图通常需要按规定的大小进行统一分幅,并进行系统的编号。地形图的分幅可分为两大类:一是按经纬度进行分幅,称为梯形分幅法,一般用于国家基本比例尺系列的地形图;二是按平面直角坐标进行分幅,称为矩形分幅法,一般用于大比例…

地心地固坐标系WGS84经纬度与笛卡尔直角坐标系相互转换的推导、理解与代码实现(C++和matlab)

目录一、大地坐标系现状简析1.1 我国1.2 美国1.3 日本二、经纬度坐标&#xff08;L,B,H&#xff09;与直角坐标系坐标&#xff08;X,Y,Z&#xff09;相互转换2.1 正解&#xff08;L,B,H&#xff09;——>&#xff08;X,Y,Z&#xff09;2.1.1 数学推导2.1.2 C代码实现2.1.3 m…

文件描述符和缓冲区

文章目录文件操作符系统调用接口文件接口的简单实用实验一&#xff1a;打开文件写入信息实验二&#xff1a;read接口读取文件返回值和fd联想到数组下标OS怎么管理文件呢&#xff1f;先描述再组织。一切皆文件012的操作文件描述符的分配规则&#xff1a;输出重定向的原理。追加重…

Apache Kyuubi、Spark Thrift Server与Hive Server2

HiveServer2和Spark Thrift Server HiveServer2和Spark Thrift Server&#xff0c;两者其实都是提供一个常驻的SQL服务&#xff0c;用来对外提供高性能的SQL引擎能力&#xff0c;不过两者又有些偏差&#xff0c;主要是HS2是独立的Server&#xff0c;可组成集群&#xff0c;而S…

【进阶】C语言第三课:升级你的指针(2)

目录 &#x1f347;前言&#x1f347;&#xff1a; 一、数组参数&#x1f920;&#xff1a; 1.一维数组传参&#x1f348;&#xff1a; 2.二维数组传参&#x1f349;&#xff1a; 二、指针参数&#x1f929;&#xff1a; 1.一级指针传参&#x1f34a;&#xff1a; 2.二级指针…