FOC: [2] Verilog implementation and simulation of SVPWM (seven-segment)

Sui Sui Nian: After nearly a week of debugging and error checking (sorry, I am too good at it), I finally realized Verilog's implementation of the SVPWM algorithm based on the MATLAB code, and at the same time gave the simulation results.

Update on October 20, 2022 : I am really sorry, because I did not consider the quantization of the input voltage value and the dead time in the algorithm before, I also discovered this error during the circuit test process, and corrected and modified it today. For the specific principle of voltage value quantification, please refer to my previous article~

Table of contents

1 main idea

2 module code

2.1 my_SVPWM module

2.2 Jud_sec module

2.3 Cal_time module

2.4 Switch_time module

2.5 Tri_gener module

2.6 Test Module

3 Simulation results

3.1 MATLAB calculation results

3.2 Vivado 2018.3 Simulation Results


1 main idea

The idea is basically the same as the previous article, but for some features in Verilog (including accuracy and timing), this article gives the code of each module and the simulation results of Vivado.

Readers can compare with the previous article by themselves: FOC: [1] Analysis of SVPWM algorithm (seven-segment) and MATLAB simulation verification

2 module code

2.1 my_SVPWM module

Used to realize SVPWM, the input is Vα and Vβ after Park inverse transformation, and the output is the control signal of the three bridge arms.


// SVPWM模块
// Type    : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能?? 用来实现SVPWM,输入是Park 逆变换后的Vα与Vβ,输出是三个桥臂的控制信号
// 包含四个部分:
			// 01 扇区判断  
			// 02 矢量作用时间计算
			// 03 逆变器开关切换时间计算
			// 04 利用三角波改变开关状态


module my_SVPWM(
	input  wire               clk,         //时钟信号
	input  wire               rstn,        //复位信号
	input  wire               in_en,       //系统的输入使能信号
	input  wire signed [15:0] Valpha,      //Park逆变换的结果Vα
	input  wire signed [15:0] Vbeta,       //Park逆变换的结果Vβ
	output wire               pwm_a,       //SVPWM的输出信号   PWM_a
	output wire               pwm_an,      //SVPWM的输出信号   PWM_an
	output wire               pwm_b,       //SVPWM的输出信号   PWM_b
	output wire               pwm_bn,      //SVPWM的输出信号   PWM_bn
	output wire               pwm_c,       //SVPWM的输出信号   PWM_c
	output wire               pwm_cn       //SVPWM的输出信号   PWM_cn
);

parameter  Dead_Zone = 39;    //死区的宽度


//下面的是调试版本,用来观察中间变??
 // module my_SVPWM(
 // 	input  wire               clk,         //时钟信号
 // 	input  wire               rstn,        //复位信号
 // 	input  wire               in_en,       //系统的输入使能信号
 // 	input  wire signed [15:0] Valpha,      //Park逆变换的结果Vα
 // 	input  wire signed [15:0] Vbeta,       //Park逆变换的结果Vβ
 // 	output wire               pwm_a,       //SVPWM的输入1 PWM_a
 // 	output wire               pwm_b,       //SVPWM的输入2 PWM_b
 // 	output wire               pwm_c,       //SVPWM的输入3 PWM_c
 // 	output wire        [3:0]  n,
 // 	output wire        [3:0]  sector,
 // 	output wire               Jug_sec_in_en,
 // 	output wire signed [16:0] x,
 // 	output wire signed [16:0] y,
 // 	output wire signed [16:0] z,
 // 	output wire               Jug_sec_out_en,
 // 	output wire signed [16:0] Tfirst,
 // 	output wire signed [16:0] Tsecond,
 // 	output wire signed [16:0] Tzero,
 // 	output wire               Cal_time_out_en,
 // 	output wire signed [16:0] Tcm1,
 // 	output wire signed [16:0] Tcm2,
 // 	output wire signed [16:0] Tcm3,
 // 	output wire               Switch_time_out_en,
 // 	output wire signed [11:0] Ts_cnt,
 // 	output wire               Tri_gener_out_en
 // );


// SVPWM的实例化过程------------------------------------------------------------------------------------------------------------
// 00 实例化需要使用到的导线
wire        [3:0]  n;
wire        [3:0]  sector;
wire               Jug_sec_in_en;
wire signed [63:0] x;
wire signed [63:0] y;
wire signed [63:0] z;
wire               Jug_sec_out_en;
wire signed [63:0] Tfirst;
wire signed [63:0] Tsecond;
wire signed [63:0] Tzero;
wire               Cal_time_out_en;
wire signed [63:0] Tcm1;
wire signed [63:0] Tcm2;
wire signed [63:0] Tcm3;
wire               Switch_time_out_en;
wire signed [11:0] Ts_cnt;
wire               Tri_gener_out_en;

reg                first_start;
reg         [4:0]  first_start_cnt;

//00 首次启动---------------------------------------------------------------------------------------------------------------------
always_ff @(posedge clk or negedge rstn) begin
	if(~rstn) begin
		first_start     <= 0;
		first_start_cnt <= 5'd0;
	end else begin
		if(first_start_cnt <= 20)
			first_start_cnt <= first_start_cnt + 5'd1;
		if(first_start_cnt == 18)
			first_start     <= 1;
		else
			first_start     <= 0;
	end
end



// 01 扇区判断--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:利用当前的Valpha与Vbeta判断所在的扇区
// 输入:Valpha Vbeta
// 输出:扇区数字sector,以及相关参数N

//assign Jug_sec_in_en = Tri_gener_out_en || in_en;
assign Jug_sec_in_en = Tri_gener_out_en || first_start;


Jug_sec Jug_sec(
	.clk         ( clk                ),           //时钟信号
	.rstn        ( rstn               ),           //复位信号
	.in_en       ( Jug_sec_in_en      ),           //输入有效信号
	.Valpha      ( Valpha             ),           //Park逆变换的结果Vα (是有符号数,-32768~32767)
	.Vbeta       ( Vbeta              ),           //Park逆变换的结果Vβ (是有符号数,-32768~32767)
	.n           ( n                  ),           //扇区计算中常用的N
	.sector      ( sector             ),           //扇区的结果
	.x           ( x                  ),           //就是  X
	.y           ( y                  ),           //就是  Y
	.z           ( z                  ),           //就是  Z
	.out_en      ( Jug_sec_out_en     )            //输出使能信号
);



// 02 矢量作用时间计算--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:矢量作用时间计算
// 输入: X,Y,Z三个变量以及N
// 输出: 根据N判断出的时间长度

Cal_time Cal_time(
	.clk         ( clk                ),           //时钟信号
	.rstn        ( rstn               ),           //复位信号
	.in_en       ( Jug_sec_out_en     ),           //输入使能信号
	.x           ( x                  ),
	.y           ( y                  ),
	.z           ( z                  ),
	.n           ( n                  ),
	.Tfirst      ( Tfirst             ),
	.Tsecond     ( Tsecond            ),
	.Tzero       ( Tzero              ),
    .out_en      (                    ),           //输出使能信号
	.out_en2     (                    ),
	.out_en3     ( Cal_time_out_en    )
);




// 03 计算逆变器开关切换的时间--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:利用查表的方式,计算三个相开关切换的时间
// 输入:
// 输出:

Switch_time Switch_time(
	.clk         ( clk                ),           //时钟信号
	.rstn        ( rstn               ),           //复位信号
	.in_en       ( Cal_time_out_en    ),           //输入使能信号
	.n           ( n                  ),
	.Tfirst      ( Tfirst             ),
	.Tsecond     ( Tsecond            ),
	.Tzero       ( Tzero              ),
	.Tcm1        ( Tcm1               ),           //三个逆变器的切换时间
	.Tcm2        ( Tcm2               ),
	.Tcm3        ( Tcm3               ),
	.out_en      (                    ),           //输出使能信号 
	.out_en2     ( Switch_time_out_en )
);



// 04 产生三角??--------------------------------------------------------------------------------------------------------------------------------------------------
// 功能:绘制三角波
// 输入:
// 输出:

Tri_gener Tri_gener(
    .clk         ( clk                ),           //输入时钟
    .rst         ( rstn               ),           //复位信号
    .in_en       ( Switch_time_out_en ),           //输入使能信号
    .Ts_cnt      ( Ts_cnt             ),           //Ts的计数器,用来产生一个周期为Ts=2*Tp的三角波
    .deta_clk    ( Tri_gener_out_en   )            //每次输出一个时钟,就给一个高电平
);


// 05 结合三角波,产生SVPWM结果(包含死区的计算)
// 功能:结合三角波,产生SVPWM的结果
// 输入: 计算出来的输出波形切换时间Tcm1,Tcm2,Tcm3,以及当前的
// 输出: 


assign pwm_a = (Ts_cnt >=  Tcm1) ? 1:0;
assign pwm_b = (Ts_cnt >=  Tcm2) ? 1:0;
assign pwm_c = (Ts_cnt >=  Tcm3) ? 1:0;

// 06 死区时间的考虑,这里由于我电路中需要给同相信号,所以是下面的表达式
//    要根据实际电路确定正负,但是这个延时的思路是相同的
assign pwm_an = (Ts_cnt >=  Tcm1-Dead_Zone) ? 1:0;
assign pwm_bn = (Ts_cnt >=  Tcm2-Dead_Zone) ? 1:0;
assign pwm_cn = (Ts_cnt >=  Tcm3-Dead_Zone) ? 1:0;

endmodule

2.2 Jud_sec module

It is used to realize that the input is Vα and Vβ after Park inverse transformation, and the value of the output sector.

The details that need to be noted here are that the calculation of n needs to refer to three expressions, but in fact only the specific symbols of the expressions need to be used, not the values. Therefore, the fractional part can be amplified by multiplication, so as to obtain a more accurate judgment of positive and negative signs.

Correspondingly, for the calculation of XYZ, specific values ​​are required, and the precision is high. How to implement floating-point arithmetic? A better method is to implement all the multiplications first, and then perform all the divisions, so that better floating-point precision can be obtained.


// 用来实现扇区的判断
// Type    : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 用来实现输入是Park 逆变换后的Vα与Vβ,输出扇区的值

module Jug_sec(
	input  wire                clk,         //时钟信号
	input  wire                rstn,        //复位信号
	input  wire                in_en,       //输入有效信号
	input  wire signed [15:0]  Valpha,      //Park逆变换的结果Vα (是有符号数,-32768~32767)
	input  wire signed [15:0]  Vbeta,       //Park逆变换的结果Vβ (是有符号数,-32768~32767)
	output reg         [3:0]   n,           //扇区计算中常用的N
	output reg         [3:0]   sector,      //扇区的结果
	output reg  signed [63:0]  x,           //就是X
	output reg  signed [63:0]  y,           //就是Y
	output reg  signed [63:0]  z,           //就是Z
	output reg                 out_en

);


//reg [16:0] Vref1; // 不需要定义,就是 Vbeta本身
reg signed [31:0] Vref1;
reg signed [31:0] Vref2;
reg signed [31:0] Vref3;
//reg en_flag;
reg flag2;
//reg [16:0] y;
//reg [16:0] z;

wire signed [31:0] alphasqrt3;
parameter  Ts      = 1666;
parameter  sqrt3Ts = 2884; 
parameter  Vdc     = 78643;    //V_dc_real/V_RANGE*32768;
//parameter  temp = sqrt3Ts/Vdc;


always @(*)
begin
	if(~rstn) 
	begin
		n      <= 4'b0000;
	end
	else
	begin
 		n[2:0]<= {~Vref3[31], ~Vref2[31], ~Vbeta[15]};		
	end
end


always@(posedge clk)
if(~rstn)
begin
		Vref1  <= 32'd0;
		Vref2  <= 32'd0;
		Vref3  <= 32'd0;
		x      <= 64'd0;
		y      <= 64'd0;
		z      <= 64'd0;
		sector <= 4'b0000;
		flag2  <= 1'd0;
		out_en <= 1'd0;
end

else
begin
		if(flag2 == 1'd1)
		begin
			flag2  <= 1'd0;
	 		case(n)  //通过符号来判断
	 		
	 		4'd3: //3
	 		begin
	 			sector <= 4'd1;
	 			out_en <= 1'd1;
	 		end

	 		4'd1:
	 		begin
	 			sector <= 4'd2;
	 			out_en <= 1'd1;
	 		end

	 		4'd5:
	 		begin
				sector <= 4'd3;
				out_en <= 1'd1;
	 		end

	  		4'd4:
	 		begin
				sector <= 4'd4;
				out_en <= 1'd1;
	 		end

	 		4'd6:
	 		begin
				sector <= 4'd5;
				out_en <= 1'd1;
	 		end

	 		4'd2:
	 		begin
	 			sector <= 4'd6;
	 			out_en <= 1'd1;
	 		end 		 				 		

	 		default:
	 		begin
	 			sector <= 4'd0;
	 			out_en <= 1'd0;
	 		end

	 		endcase
	 	end

 		if(out_en)
 		begin
 			$display("OUTPUT:");   
 			$display("      sector  : %d",sector);
            $display("      n       : %d",n);
            $display("      x       : %d",x);
            $display("      y       : %d",y);
            $display("      z       : %d",z);   			
 			out_en<= 1'd0;
 		end

 		if(in_en)
		begin
		//实现高精度的方法,先计算所有的乘法,最后计算除法!
		//对于只需要计算符号的,不需要除以分母啦
        $display("--------------------05 SVPWM--------------------");
        $display("          ----------05-01 Judge Section----------          ");
        $display("INPUT:");     
        $display("      Valpha  : %d",Valpha);
        $display("      Vbeta   : %d",Vbeta);
		Vref1 <= Vbeta;         
		Vref2 <= (-1*Vbeta*512 + Valpha*887);///1024; //这一步,相当于alphasqrt3*根号三去掉后面的几位就是实现了除以255(新策略,都乘以256,再除以512)
 		Vref3 <= (-1*Vbeta*512 - Valpha*887);///1024; 

 		x     <= sqrt3Ts*(Vbeta)/Vdc;
 		y     <= (sqrt3Ts*Vbeta*512 + sqrt3Ts*Valpha*887)/(1024*Vdc);  //这里与Vref是差倍数的
 		z     <= (sqrt3Ts*Vbeta*512 - sqrt3Ts*Valpha*887)/(1024*Vdc);
 		flag2 <= 1'd1;
 		end

end

endmodule

2.3 Cal_time module

Using the input XYZ, calculate the length of time.


// 计算时间的长度
// Type    : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 利用输入的XYZ,计算时间的长度

module Cal_time(
	input  wire                clk,         //时钟信号
	input  wire                rstn,        //复位信号
	input  wire                in_en,       //输入使能信号

	input  wire signed [63:0]  x,
	input  wire signed [63:0]  y,
	input  wire signed [63:0]  z,

	input  wire        [3:0]   n,

	output reg  signed [63:0]   Tfirst,
	output reg  signed [63:0]   Tsecond,
	output reg  signed [63:0]   Tzero,
    output reg                  out_en,      //输出使能信号
    output reg                  out_en2,
    output reg                  out_en3,
    output reg  signed [63:0]   temp2,
    output reg  signed [63:0]   temp3,
    output reg  signed [63:0]   temp                


    //output reg                  flag2
);


parameter  Ts = 1666;


//reg  signed [30:0]   temp2;
//reg  signed [30:0]   temp3;
reg                  flag2;
//wire signed [30:0]   temp;

//assign  = 



always @(posedge clk)
begin
	if(in_en)
		begin
	        $display("--------------------05 SVPWM--------------------");
	        $display("          ----------05-02 Calculate Time----------          ");
	        $display("INPUT:"); 
	        $display("      x  : %d",x);
        	$display("      y  : %d",y);    
        	$display("      z  : %d",z);    
        	
			flag2 <= 1'd1;
		end

	if(~rstn) 
	begin
		Tfirst  <= 64'd0;
		Tsecond <= 64'd0;
		temp2   <= 64'd0;
		temp3   <= 64'd0;
		flag2   <= 1'd0;
		out_en  <= 1'd0;
		out_en2 <= 1'd0;
		out_en3 <= 1'd0;
		Tzero   <= 64'd0;
		temp    <= 64'd0;
	end

	else
		
		Tzero <= (Ts - Tfirst - Tsecond)/2;	
		begin
			if(flag2)
				begin
				flag2 <= 1'd0;
					case(n)
						4'd1:begin
							Tfirst  <= z;
							Tsecond <= y;
							out_en <= 1'd1;
						end

						4'd2:begin
							Tfirst  <= y;
							Tsecond <= -1*x;
							out_en <= 1'd1;
						end
						
						4'd3:begin
							Tfirst  <= -1*z;
							Tsecond <= x;
							out_en <= 1'd1;
						end
						
						4'd4:begin
							Tfirst  <= -1*x;
							Tsecond <= z;
							out_en <= 1'd1;
						end
						
						4'd5:begin
							Tfirst  <= x;
							Tsecond <= -1*y;
							out_en <= 1'd1;
						end
						
						4'd6:begin
							Tfirst  <= -1*y;
							Tsecond <= -1*z;
							out_en <= 1'd1;
						end

						default:
						begin
							Tfirst  <= 17'd0;
							Tsecond <= 17'd0;
							out_en <= 1'd0;			
						end

					endcase 
				end



		if(out_en)
			begin
				out_en <= 1'd0;
				out_en2<= 1'd1;  
			end	

		if(out_en2)
		begin
			out_en2 <= 1'd0;
			out_en3 <= 1'd1;
		end

		if(out_en3)
		begin
			out_en3 <= 1'd0;
	        $display("OUTPUT:"); 
	        $display("      Tfirst  : %d",Tfirst);
        	$display("      Tsecond : %d",Tsecond);    
        	$display("      Tzero   : %d",Tzero); 
		end		


		end

	if(Tfirst + Tsecond > Ts)
	begin
		//temp2   <= Ts*Tfirst;
		//temp3   <= Ts*Tsecond;
		Tfirst  <= Ts*Tfirst/(Tfirst + Tsecond);
		Tsecond <= Ts*Tsecond/(Tfirst + Tsecond);
	end	
	
end

endmodule

2.4 Switch_time module

The time length information is used to calculate the specific switching moment of the switch.


// 计算逆变器信号改变的时间
// Type    : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 利用时间长度信息,计算具体的开关切换时刻。

module Switch_time(
	input  wire                 clk,         //时钟信号
	input  wire                 rstn,        //复位信号
	input  wire                 in_en,       //输入使能信号
	input  wire        [3:0]    n,
	input  wire signed [63:0]   Tfirst,
	input  wire signed [63:0]   Tsecond,
	input  wire signed [63:0]   Tzero,

	output reg  signed [63:0]   Tcm1,        //三个逆变器的切换时间
	output reg  signed [63:0]   Tcm2,
	output reg  signed [63:0]   Tcm3,
	output reg                  out_en,      //输出使能信号 
	output reg                  out_en2      //延迟一拍
);

wire signed [63:0] Ta_wire;
wire signed [63:0] Tb_wire;
wire signed [63:0] Tc_wire;

assign Ta_wire = Tzero/2;
assign Tb_wire = Ta_wire + Tfirst/2;
assign Tc_wire = Tb_wire + Tsecond/2;

reg signed [63:0] Ta;
reg signed [63:0] Tb;
reg signed [63:0] Tc;
reg               flag2;




always @(*)
begin
  if(!rstn)
  begin
  	Ta      <= 64'd0;
  	Tb      <= 64'd0;
  	Tc      <= 64'd0;  	
  end
  else
  begin
  	Ta      <= Ta_wire;
  	Tb      <= Tb_wire;
  	Tc      <= Tc_wire;   	
  end

	if(out_en2)	
	begin
  	Ta      <= 64'd0;
  	Tb      <= 64'd0;
  	Tc      <= 64'd0;
	end

end









always @(posedge clk) //
begin
	if(in_en)
	begin
		flag2 <= 1'd1;
    $display("--------------------05 SVPWM--------------------");
    $display("          ----------05-03 Switch Time----------          ");
    $display("INPUT:"); 
    $display("      Tfirst  : %d",Tfirst);
  	$display("      Tsecond : %d",Tsecond);    
  	$display("      Tzero   : %d",Tzero);  
	end

  if(!rstn)
  begin
  	Tcm1    <= 64'd0;
  	Tcm2    <= 64'd0;
  	Tcm3    <= 64'd0;

  	flag2   <= 1'd0;
  	out_en  <= 1'd0;
  	out_en2 <= 1'd0;
  end

  else
  begin
  	if(flag2)
  	begin
  		
	  	// Ta    <= Tzero/2;
	  	// Tb    <= Ta + Tfirst/2;
	  	// Tc    <= Tb + Tsecond/2;

			case(n)
			4'd1:begin
				Tcm1   <= Tb;
				Tcm2   <= Ta;
				Tcm3   <= Tc;
				out_en <= 1'd1;
			end

			4'd2:begin
				Tcm1   <= Ta;
				Tcm2   <= Tc;
				Tcm3   <= Tb;
				out_en <= 1'd1;
			end
			
			4'd3:begin
				Tcm1   <= Ta;
				Tcm2   <= Tb;
				Tcm3   <= Tc;
				out_en <= 1'd1;
			end
			
			4'd4:begin
				Tcm1   <= Tc;
				Tcm2   <= Tb;
				Tcm3   <= Ta;
				out_en <= 1'd1;
			end
			
			4'd5:begin
				Tcm1   <= Tc;
				Tcm2   <= Ta;
				Tcm3   <= Tb;
				out_en <= 1'd1;
			end
			
			4'd6:begin
				Tcm1   <= Tb;
				Tcm2   <= Tc;
				Tcm3   <= Ta;
				out_en <= 1'd1;
			end

			default:
			begin
				Tcm1   <= Tb;
				Tcm2   <= Ta;
				Tcm3   <= Tc;
				out_en <= 1'd0;				
			end

		endcase 
		end

		if(out_en)
			begin
				out_en  <= 1'd0;
				flag2   <= 1'd0;
			end

		if(out_en)	
		begin
			out_en2 <= 1'd1;
			$display("OUTPUT:");
	    $display("      Tcm1  : %d",Tcm1);
	  	$display("      Tcm2  : %d",Tcm2);    
	  	$display("      Tcm3  : %d",Tcm3);			 
		end

		if(out_en2)	
		begin
			out_en2 <= 1'd0;
	  	// Ta      <= 17'd0;
	  	// Tb      <= 17'd0;
	  	// Tc      <= 17'd0;
	  	flag2   <= 1'd0;
	  	out_en  <= 1'd0;
		end

  end

end

endmodule

2.5 Tri_gener module

Generate a triangle wave, which is convenient for determining the current moment. The variable CYCLE_NUM can be used to control the specific number of cycles output by SVPWM during simulation.


// 三角波生成模块
// Type    : synthesizable
// Standard: SystemVerilog 2005 (IEEE1800-2005)
// 功能: 产生三角波,便于确定当前所处的时刻。

module Tri_gener(
      input   wire         clk,              //输入时钟
      input   wire         rst,              //复位信号
      input   wire         in_en,            //输入使能信号
      // output  reg          Ts_cp,         //对PGFS输入时钟进行同步化后的时钟,提供给Ts累加的脉冲
      output  reg   signed [11:0]  Ts_cnt,          //Ts的计数器,用来产生一个周期为Ts=2*Tp的三角波
      output  reg          deta_clk          //每次输出一个时钟,就给一个高电平
);


///三角波产生//
reg [16:0]         adder;   //Ts有关的相位累加器
reg                Ts_dir;  //Ts的计数器的计数方向
reg                flag2;
reg signed [5:0]   cycle_num; //计数完成了多少个周期

parameter   Tp = 833;   //开关周期的一半;实际是833个周期,是0-832,
//            pfs= 10000;  //频率控制输入,5000:7K,10000:15K,20000:30K
// 1 2 3 4 5 1 2 3 4 5
// 0 1 2 1   0 1 2 1 0 
// 1 2 3 4 5 6 1 2 3 4 5
// 0 1 2 3 2 1 0 1 2 1 0


parameter   CYCLE_NUM = 3;
            

always @(posedge clk)
begin
    if(in_en)
        begin
            flag2 <= 1'd1;
        end

  if(!rst)
  begin
    Ts_cnt   <= 12'd0;
    Ts_dir   <= 1'b1;
    deta_clk <= 1'b0;
    flag2    <= 1'd0;
    cycle_num<= 1'd0; 
  end
  else
  begin
    // if(cycle_num == CYCLE_NUM)
    //     flag2 <= 1'd0;

    if(flag2)
        begin
            if(Ts_dir)
                Ts_cnt <= Ts_cnt + 12'b1;
            else
                Ts_cnt <= Ts_cnt - 12'b1;

            if( Ts_cnt == Tp-1 ) //注意是非阻塞赋值
                begin
                    Ts_dir   <= 1'b0;
                end

            if( Ts_cnt == 1 )   //注意是非阻塞赋值
                begin
                    Ts_dir   <= 1'b1;
                end

            if( Ts_cnt == 1 && ~Ts_dir)
                begin
                   deta_clk  <= 1'b1;
                   cycle_num <= cycle_num + 1'b1;
                end
            else
                begin
                   deta_clk <= 1'b0; 
                end
        end


  end
end

endmodule

2.6 Test Module

This is the test code for the overall module, where you can set the values ​​of Vα and Vβ, and perform simulation verification in Quartus.

// Verilog Test Bench template for design : my_SVPWM

`timescale 1 ps/ 1 ps
module my_SVPWM_vlg_tst();
// constants                                           
// general purpose registers
reg clk;
reg rstn;
// reg in_en;
// test vector input registers
reg signed [15:0] Valpha;
reg signed [15:0] Vbeta;
// wires                                               
// wire Cal_time_out_en;
// wire Jug_sec_in_en;
// wire Jug_sec_out_en;
// wire Switch_time_out_en;
// wire [16:0]  Tcm1;
// wire [16:0]  Tcm2;
// wire [16:0]  Tcm3;
// wire [16:0]  Tfirst;
// wire Tri_gener_out_en;
// wire signed [11:0]  Ts_cnt;
// wire [16:0]  Tsecond;
// wire [16:0]  Tzero;
// wire [3:0]  n;
wire Tim1_Ch1;         // SVPWM处理后的结果Ch1
wire Tim1_Ch1N;        // SVPWM处理后的结果Ch1N,(电路层已经和CH1连接)
wire Tim1_Ch2;         // SVPWM处理后的结果Ch2
wire Tim1_Ch2N;        // SVPWM处理后的结果Ch2N,(电路层已经和CH2连接)
wire Tim1_Ch3;         // SVPWM处理后的结果Ch3
wire Tim1_Ch3N;        // SVPWM处理后的结果Ch3N,(电路层已经和CH3连接) 
// wire [3:0]  sector;
// wire [16:0]  x;
// wire [16:0]  y;
// wire [16:0]  z;

parameter half_cycle = 10;

// assign statements (if any)                          
// my_SVPWM i1 (
// // port map - connection between master ports and signals/registers   
// 	.Cal_time_out_en(Cal_time_out_en),
// 	.Jug_sec_in_en(Jug_sec_in_en),
// 	.Jug_sec_out_en(Jug_sec_out_en),
// 	.Switch_time_out_en(Switch_time_out_en),
// 	.Tcm1(Tcm1),
// 	.Tcm2(Tcm2),
// 	.Tcm3(Tcm3),
// 	.Tfirst(Tfirst),
// 	.Tri_gener_out_en(Tri_gener_out_en),
// 	.Ts_cnt(Ts_cnt),
// 	.Tsecond(Tsecond),
// 	.Tzero(Tzero),
// 	.Valpha(Valpha),
// 	.Vbeta(Vbeta),
// 	.n(n),
// 	.pwm_a(pwm_a),
// 	.pwm_b(pwm_b),
// 	.pwm_c(pwm_c),
// 	.sector(sector),
// 	.x(x),
// 	.y(y),
// 	.z(z),
// 	.clk(clk),
// 	.rstn(rstn),
// 	.in_en(in_en)
// );

my_SVPWM i1 (
    .clk      ( clk         ), //时钟信号
    .rstn     ( rstn        ), //复位信号
    .in_en    ( ~rstn       ), //系统的输入使能信号
    .Valpha   ( Valpha      ), //Park逆变换的结果Vα
    .Vbeta    ( Vbeta       ), //Park逆变换的结果Vβ
    .pwm_a    ( Tim1_Ch1    ), //SVPWM的输出1 PWM_a
    .pwm_an   ( Tim1_Ch1N   ), //SVPWM的输出1 PWM_an
    .pwm_b    ( Tim1_Ch2    ), //SVPWM的输出2 PWM_b
    .pwm_bn   ( Tim1_Ch2N   ), //SVPWM的输出2 PWM_bn
    .pwm_c    ( Tim1_Ch3    ), //SVPWM的输出3 PWM_c
    .pwm_cn   ( Tim1_Ch3N   )  //SVPWM的输出3 PWM_cn
);

initial                                                
begin                                                  
// code that executes only once                        
// insert code here --> begin                          
	clk = 0;
    forever begin
		#half_cycle clk = 1;
        #half_cycle clk = 0;
	end                                                          
// --> end                                             
$display("Running testbench");                       
end                               

initial
begin
	rstn = 1;
	#5 rstn = 0;
	#10 rstn = 1;
end

initial
begin
	Valpha = 16'd9830;
    Vbeta  = -1*16'd26214;
end


// initial
// begin
// 	in_en = 0;
// 	#90 in_en = 1;
// 	#20 in_en = 0;
// end
                                                 
endmodule

3 Simulation results

The simulation verification has been carried out by Quartus. The specific simulation results are shown below and correspond to the results of MATLAB. It can be seen that the two are completely consistent, which proves that the algorithm is correct.

The example tested here is the modulation result when U_alpha_real = 3, U_beta_real = -8~

3.1 MATLAB calculation results

Specific data results:

Output waveform result:

3.2 Vivado 2018.3 Simulation Results

Here's an overall picture:

Another detailed picture:

It can be seen that it is consistent with the output of MATLAB.

It is worth mentioning that some readers may ask, why the value of XYZ, the calculation results of MATLAB and Verilog just double? This is because the problem lies in Ts. In MATLAB, a specific time period length is used (the count value is divided by the 50MHz system clock), while in Verilog, since SVPWM only needs to be ordered in a fixed period according to requirements The modulation waveform is output, so the specific cycle length does not affect (the duty ratio is more important), so the value of the Ts count value is simply used instead of the specific cycle length.

Update on October 20, 2022: The above paragraph is for the Quartus simulation results of the previous code (for the previous blog, MATLAB does not consider the voltage quantization situation. After today’s update, because I have been based on the specific cycle from time to time , using the corresponding actual Ts value of 1666, this problem no longer exists, you can ignore this paragraph~)


This is the whole content of this issue. If you like my article, don't forget to like and collect it, and share it with your friends~

Guess you like

Origin blog.csdn.net/Alex497259/article/details/125502416