Below is my top-level module:
module spwm(clk, p1, sine_a, tri_out);
input clk; //16MHz
reg tick = 0;
reg [7:0] theta_a = 8'd0;
reg [7:0] theta_tri = 8'd0;
output [8:0] sine_a;
output [9:0] tri_out;
integer div = 42;
integer r = 0;
output p1;
reg p1;
always@(posedge clk) begin
if(r == div) begin
tick = ~tick;
r = 1;
end
else begin
r = r + 1;
end
end
always@(tick) begin
theta_a = (theta_a + 8'b1) % 8'd255;
end
always@(posedge clk) begin
theta_tri = (theta_tri + 8'b1) % 8'd255;
end
//generate spwm
always@(clk) begin
if ($signed(sine_a) > $signed(tri_out))
p1 = 1'b1;
else if ($signed(sine_a) < $signed(tri_out))
p1 = 1'b0;
end
SINE_LUT lut_a(theta_a, sine_a);
TRI_LUT lut_tri(theta_tri, tri_out);
endmodule
My Sine Wave look-up table:
module SINE_LUT (THETA, SINE_OUT);
input [7:0] THETA; //Input THETA
output [8:0] SINE_OUT; //Output SINE_OUT
reg [5:0] THETA_TMP; //Lower bits of THETA (counting up or counting down)
reg [6:0] THETA_HLP; //Helper for reversing lower bits of counting direction for theta
reg [8:0] SINE_TMP; //Temporary holder for output (two's compliment)
reg [8:0] SINE_OUT; //Output Sine in two's compliment
always @ (THETA) begin //Combinatorial Logic Look Up Table
if (THETA[6:0] == 7'd64) begin //At 90 degrees and 270 degrees
SINE_TMP = 9'd255;
end
else begin
if (THETA[6]) begin //If counting down should begin reverse counting order
THETA_HLP = 7'd64 - {1'd0, THETA[5:0]};
THETA_TMP = {THETA_HLP[5:0]};
end
else begin //Continue counting up by default
THETA_TMP = {THETA[5:0]};
end
case (THETA_TMP) //Look Up Table (quarter wave)
6'd0: SINE_TMP = 9'd0;
6'd1: SINE_TMP = 9'd6;
6'd2: SINE_TMP = 9'd13;
6'd3: SINE_TMP = 9'd19;
6'd4: SINE_TMP = 9'd25;
6'd5: SINE_TMP = 9'd31;
6'd6: SINE_TMP = 9'd37;
6'd7: SINE_TMP = 9'd44;
6'd8: SINE_TMP = 9'd50;
6'd9: SINE_TMP = 9'd56;
6'd10: SINE_TMP = 9'd62;
6'd11: SINE_TMP = 9'd68;
6'd12: SINE_TMP = 9'd74;
6'd13: SINE_TMP = 9'd80;
6'd14: SINE_TMP = 9'd86;
6'd15: SINE_TMP = 9'd92;
6'd16: SINE_TMP = 9'd98;
6'd17: SINE_TMP = 9'd103;
6'd18: SINE_TMP = 9'd109;
6'd19: SINE_TMP = 9'd115;
6'd20: SINE_TMP = 9'd120;
6'd21: SINE_TMP = 9'd126;
6'd22: SINE_TMP = 9'd131;
6'd23: SINE_TMP = 9'd136;
6'd24: SINE_TMP = 9'd142;
6'd25: SINE_TMP = 9'd147;
6'd26: SINE_TMP = 9'd152;
6'd27: SINE_TMP = 9'd157;
6'd28: SINE_TMP = 9'd162;
6'd29: SINE_TMP = 9'd167;
6'd30: SINE_TMP = 9'd171;
6'd31: SINE_TMP = 9'd176;
6'd32: SINE_TMP = 9'd180;
6'd33: SINE_TMP = 9'd185;
6'd34: SINE_TMP = 9'd189;
6'd35: SINE_TMP = 9'd193;
6'd36: SINE_TMP = 9'd197;
6'd37: SINE_TMP = 9'd201;
6'd38: SINE_TMP = 9'd205;
6'd39: SINE_TMP = 9'd208;
6'd40: SINE_TMP = 9'd212;
6'd41: SINE_TMP = 9'd215;
6'd42: SINE_TMP = 9'd219;
6'd43: SINE_TMP = 9'd222;
6'd44: SINE_TMP = 9'd225;
6'd45: SINE_TMP = 9'd228;
6'd46: SINE_TMP = 9'd231;
6'd47: SINE_TMP = 9'd233;
6'd48: SINE_TMP = 9'd236;
6'd49: SINE_TMP = 9'd238;
6'd50: SINE_TMP = 9'd240;
6'd51: SINE_TMP = 9'd242;
6'd52: SINE_TMP = 9'd244;
6'd53: SINE_TMP = 9'd246;
6'd54: SINE_TMP = 9'd247;
6'd55: SINE_TMP = 9'd249;
6'd56: SINE_TMP = 9'd250;
6'd57: SINE_TMP = 9'd251;
6'd58: SINE_TMP = 9'd252;
6'd59: SINE_TMP = 9'd253;
6'd60: SINE_TMP = 9'd254;
6'd61: SINE_TMP = 9'd254;
6'd62: SINE_TMP = 9'd255;
6'd63: SINE_TMP = 9'd255;
endcase
end
if (THETA > 8'd128) begin //Any theta between 180 and 360 degrees should be negative
SINE_OUT = (~SINE_TMP) + 1'd1; //Create negative value in two's compliment
end
else begin //Any theta between 0 and 180 degrees should be positive
SINE_OUT = SINE_TMP;
end
end
endmodule
I also used lookup table for triangular wave:
module TRI_LUT (THETA, TRI_OUT);
input [7:0] THETA;
output [9:0] TRI_OUT;
reg [5:0] THETA_TMP;
reg [6:0] THETA_HLP;
reg [9:0] TRI_TMP;
reg [9:0] TRI_OUT;
always @ (THETA) begin
if (THETA[6:0] == 7'd64) begin
TRI_TMP = 10'd364;
end
else begin
if (THETA[6]) begin
THETA_HLP = 7'd64 - {1'd0, THETA[5:0]};
THETA_TMP = {THETA_HLP[5:0]};
end
else begin
THETA_TMP = {THETA[5:0]};
end
case (THETA_TMP)
6'd0: TRI_TMP = 10'd0;
6'd1: TRI_TMP = 10'd6;
6'd2: TRI_TMP = 10'd12;
6'd3: TRI_TMP = 10'd17;
6'd4: TRI_TMP = 10'd23;
6'd5: TRI_TMP = 10'd29;
6'd6: TRI_TMP = 10'd35;
6'd7: TRI_TMP = 10'd40;
6'd8: TRI_TMP = 10'd46;
6'd9: TRI_TMP = 10'd52;
6'd10: TRI_TMP = 10'd58;
6'd11: TRI_TMP = 10'd64;
6'd12: TRI_TMP = 10'd69;
6'd13: TRI_TMP = 10'd75;
6'd14: TRI_TMP = 10'd81;
6'd15: TRI_TMP = 10'd87;
6'd16: TRI_TMP = 10'd92;
6'd17: TRI_TMP = 10'd98;
6'd18: TRI_TMP = 10'd104;
6'd19: TRI_TMP = 10'd110;
6'd20: TRI_TMP = 10'd116;
6'd21: TRI_TMP = 10'd121;
6'd22: TRI_TMP = 10'd127;
6'd23: TRI_TMP = 10'd133;
6'd24: TRI_TMP = 10'd139;
6'd25: TRI_TMP = 10'd144;
6'd26: TRI_TMP = 10'd150;
6'd27: TRI_TMP = 10'd156;
6'd28: TRI_TMP = 10'd162;
6'd29: TRI_TMP = 10'd168;
6'd30: TRI_TMP = 10'd173;
6'd31: TRI_TMP = 10'd179;
6'd32: TRI_TMP = 10'd185;
6'd33: TRI_TMP = 10'd191;
6'd34: TRI_TMP = 10'd196;
6'd35: TRI_TMP = 10'd202;
6'd36: TRI_TMP = 10'd208;
6'd37: TRI_TMP = 10'd214;
6'd38: TRI_TMP = 10'd220;
6'd39: TRI_TMP = 10'd225;
6'd40: TRI_TMP = 10'd231;
6'd41: TRI_TMP = 10'd237;
6'd42: TRI_TMP = 10'd243;
6'd43: TRI_TMP = 10'd248;
6'd44: TRI_TMP = 10'd254;
6'd45: TRI_TMP = 10'd260;
6'd46: TRI_TMP = 10'd266;
6'd47: TRI_TMP = 10'd272;
6'd48: TRI_TMP = 10'd277;
6'd49: TRI_TMP = 10'd283;
6'd50: TRI_TMP = 10'd289;
6'd51: TRI_TMP = 10'd295;
6'd52: TRI_TMP = 10'd300;
6'd53: TRI_TMP = 10'd306;
6'd54: TRI_TMP = 10'd312;
6'd55: TRI_TMP = 10'd318;
6'd56: TRI_TMP = 10'd324;
6'd57: TRI_TMP = 10'd329;
6'd58: TRI_TMP = 10'd335;
6'd59: TRI_TMP = 10'd341;
6'd60: TRI_TMP = 10'd347;
6'd61: TRI_TMP = 10'd352;
6'd62: TRI_TMP = 10'd358;
6'd63: TRI_TMP = 10'd364;
endcase
end
if (THETA > 8'd128) begin
TRI_OUT = (~TRI_TMP) + 1'd1;
end
else begin
TRI_OUT = TRI_TMP;
end
end
endmodule
The above SPWM works in ModelSim PE Student Edition 10.3.
When loaded in GFEC Max II Starter Kit, the signal p1 is not modulated. Any help. Thanks...
theta_a
and p1
are inferring latching logic. This is usually synthesizes to be big, complex, brittle logic. Making them flip-flops should remove your issue.
always@(posedge clk) begin
if(r == div) begin
//tick <= ~tick; // tick can be optimized out
theta_a <= (theta_a + 8'b1) % 8'd255; // theta_a is flopped
r <= 1;
end
else begin
r <= r + 1;
end
end
always@(posedge clk) begin
theta_tri <= (theta_tri + 8'b1) % 8'd255;
end
//generate spwm
always@(posedge clk) begin // latch to flip-flop
if ($signed(sine_a) > $signed(tri_out))
p1 <= 1'b1;
else if ($signed(sine_a) < $signed(tri_out))
p1 <= 1'b0;
end
FYI: you may also want to check if there are latches in your SINE_LUT
and TRI_LUT
. If your synthesizer has a decent optimization phase, then THETA_HLP
and THETA_TMP
will be combinatorial logic. A brute force synthesizer could create latches. To guaranty combinatorial logic, make sure THETA_HLP
and THETA_TMP
are always assigned a value.
Convert:
if (THETA[6:0] == 7'd64) begin
...
end
else begin
if (THETA[6]) begin
THETA_HLP = 7'd64 - {1'd0, THETA[5:0]};
THETA_TMP = {THETA_HLP[5:0]};
end
else begin
THETA_TMP = {THETA[5:0]};
end
case (THETA_TMP)
...
endcase
end
to:
THETA_HLP = 7'd64 - {1'd0, THETA[5:0]};
THETA_TMP = THETA[6] ? THETA_HLP[5:0] : THETA[6];
if (THETA[6:0] == 7'd64) begin
...
end
else begin
case (THETA_TMP)
...
endcase
end
THETA_HLP
and THETA_TMP
are continuously assigned combinatorial logic and functionally equivalent to your original.