URL
https://opencores.org/ocsvn/pwm/pwm/trunk
Subversion Repositories pwm
Compare Revisions
- This comparison shows the changes necessary to convert path
/pwm
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/trunk/testbench/PWM_tb.v
0,0 → 1,231
//Author: Zhuxu |
//Email: m99a1@yahoo.cn |
module PWM_tb(); |
reg wb_clk=0,extclk=0; |
reg rst=1; |
|
initial #20 rst=0; |
|
always #10 wb_clk=~wb_clk; |
|
always #1 extclk=~extclk; |
|
///////test cases configuration data/////////// |
wire [31:0]configdata[0:20]; |
//PWM test 0 |
|
assign configdata[0]=32'h40303; |
assign configdata[1]=32'h600ed; |
assign configdata[2]=31'h17; |
//continous timer test 0 |
assign configdata[3]=32'h80; |
assign configdata[4]=32'h20005; |
assign configdata[5]=32'h401a1; |
assign configdata[6]=32'h1c; |
//continous timer test 1 |
assign configdata[7]=32'h1c; |
//PWM test 1 |
assign configdata[8]=32'h41d00; |
assign configdata[9]=32'h61000; |
assign configdata[10]=32'h56; |
//discontinous timer test 0 |
|
assign configdata[11]=32'h80; |
assign configdata[12]=32'h20008; |
assign configdata[13]=32'h400aa; |
assign configdata[14]=32'h15; |
//discontinous timer test 1 |
assign configdata[15]=32'h15; |
//continous timer test 2 |
assign configdata[16]=32'h80; |
assign configdata[17]=32'h20005; |
assign configdata[18]=32'h401a1; |
assign configdata[19]=32'h15; |
//continous timer test 3 |
assign configdata[20]=32'h15; |
|
|
|
//////////////////////////////////////////////// |
|
|
/////driver/////////////////////////////////// |
reg [15:0]wb_data=0; |
reg [15:0]wb_adr=0; |
reg wb_cyc=0,wb_stb=0,wb_we=0; |
wire wb_ack; |
wire [15:0]extDC; |
assign extDC=60; |
reg [4:0]nconfig=0; |
|
task driver; |
begin |
@(posedge wb_clk); |
wb_cyc<=1; |
wb_stb<=1; |
wb_we<=1; |
wb_adr<=configdata[nconfig][31:16]; |
wb_data<=configdata[nconfig][15:0]; |
nconfig<=nconfig+1; |
while(!wb_ack)begin |
@(posedge wb_clk); |
end |
wb_cyc<=0; |
wb_stb<=0; |
wb_we<=0; |
end |
endtask |
////////////////////////////////////////////// |
|
//////////////monitor///////////////////// |
wire pwm; |
reg [63:0]ct_period=0; |
reg [63:0]ct_DC=0; |
reg ready=1; |
reg [3:0]state=0; |
reg [1:0]state_mp=0; |
reg [1:0]state_mt=0; |
reg [3:0]nperiod=0; |
reg pwm_1=0; |
always@(posedge extclk) |
if(!rst)begin |
pwm_1<=pwm; |
case(state) |
0:if(wb_stb&&wb_we)begin |
case(wb_adr) |
0:if(wb_data[7])ready<=1; |
else if(wb_data[2])begin |
ready<=0; |
if(wb_data[1])state<=1; |
else state<=2; |
end |
default:ready<=1; |
endcase |
end |
1:begin |
case(state_mp) |
|
0:begin |
if(pwm&&(!pwm_1))begin |
if(nperiod==15)begin |
nperiod<=0; |
state_mp<=1; |
end |
else nperiod<=nperiod+1; |
end |
end |
1:begin |
if(pwm&&pwm_1)begin |
ct_DC<=ct_DC+2; |
ct_period<=ct_period+2; |
end |
else if((!pwm)&&pwm_1)begin |
$display("ct_DC=%d",ct_DC); |
ct_DC<=0; |
ct_period<=ct_period+2; |
end |
else if(pwm&&(!pwm_1))begin |
ct_period<=0; |
$display("ct_period=%d",ct_period); |
state_mp<=0; |
state<=0; |
ready<=1; |
end |
else ct_period<=ct_period+2; |
end |
endcase |
end |
2:begin |
if(!pwm)ct_period<=ct_period+2; |
else if(pwm&&(!pwm_1))begin |
ct_period<=0; |
$display("ct_period=%d",ct_period); |
state<=0; |
ready<=1; |
end |
end |
endcase |
end |
//////////////////////////////////////////// |
|
////////////////scoreboard///////////////////// |
reg [15:0]ctrl_sb=0; |
reg [15:0]divisor_sb=0; |
reg [15:0]period_sb=0; |
reg [15:0]DC_sb=0; |
wire [15:0]DC; |
assign DC=wb_data[6]?extDC:DC_sb; |
wire [15:0]divisor_sb_1; |
assign divisor_sb_1=(divisor_sb==0)?1:divisor_sb; |
always@(posedge wb_clk) |
if(!rst)begin |
if(wb_stb&&wb_we)begin |
case(wb_adr) |
0:begin |
ctrl_sb<=wb_data; |
if(wb_data[2])begin |
if(wb_data[1])begin |
if(wb_data[0])begin |
$display("PWM starts scoreboard: period=2*divisor_sb*period_sb=2*%d*%d=%d",divisor_sb_1,period_sb,2*divisor_sb_1*period_sb); |
$display("PWM starts scoreboard: DC=2*divisor_sb*DC_sb=2*%d*%d=%d",divisor_sb_1,DC,2*divisor_sb_1*DC); |
end |
else begin |
$display("PWM starts scoreboard: period=20*divisor_sb*period_sb=20*%d*%d=%d",divisor_sb_1,period_sb,20*divisor_sb_1*period_sb); |
$display("PWM starts scoreboard: DC=20*divisor_sb*DC_sb=20*%d*%d=%d",divisor_sb_1,DC,20*divisor_sb_1*DC); |
end |
end |
else begin |
if(wb_data[3])$write("timer starts continuous run"); |
else $write("timer starts single run"); |
if(wb_data[0])begin |
$write(" scoreboard: period=2*divisor_sb*period_sb=2*%d*%d=%d\n",divisor_sb_1,period_sb,2*divisor_sb_1*period_sb); |
end |
else begin |
$write(" scoreboard: period=20*divisor_sb*period_sb=20*%d*%d=%d\n",divisor_sb_1,period_sb,20*divisor_sb_1*period_sb); |
end |
|
end |
end |
end |
2:divisor_sb<=wb_data; |
4:period_sb<=wb_data; |
6:DC_sb<=wb_data; |
endcase |
end |
end |
///////////////////////////////////////////////// |
|
|
//test process/////////////// |
initial begin |
while(1)begin |
#1; |
if(ready&&(!rst))driver; |
end |
end |
///////////////////////////// |
|
wire [15:0]wb_o_data; |
|
PWM PWM_0( |
wb_clk, |
rst, |
wb_cyc, |
wb_stb, |
wb_we, |
wb_adr, |
wb_data, |
wb_o_data, |
wb_ack, |
extclk, |
extDC, |
1'b1, |
pwm |
); |
|
|
|
|
|
|
endmodule |
/trunk/RTL/minus_one.v
0,0 → 1,122
/*Author: Zhuxu |
m99a1@yahoo.cn |
Use parallel prefix tree structure to reduce a 16-bit number by one. |
|
stage 0: number of genration=16; number of logic operation=16; G_0[xx]=~i_operand[xx]; |
stage 1: NOG=16; NOO=8; G_1[2n-1]=G_0[2n-1]&&G_0[2n-2]; n=8:1 |
stage 2: NOG=16; NOO=7; G_2[2n-1]=G_1[2n-1]&&G_1[2n-3]; n=8:2 |
stage 3: NOG=16; NOO=6; G_3[2n-1]=G_2[2n-1]&&G_2[2n-5]; n=8:3 |
stage 4: NOG=16; NOO=4; G_4[2n-1]=G_3[2n-1]&&G_3[2n-9]; n=8:5 |
stage 5: NOG=16; NOO=7; G_5[2n]=G_4[2n]&&G_4[2n-1]; n=7:1 |
|
*/ |
module minus_one( |
input [15:0]i_operand, |
output [15:0]o_result, |
output o_borrow |
); |
//stage 0 |
wire [15:0]G_0; |
assign G_0=~i_operand; |
|
//stage 1 |
wire [15:0]G_1; |
assign G_1[1]=G_0[1]&G_0[0]; |
assign G_1[3]=G_0[3]&G_0[2]; |
assign G_1[5]=G_0[5]&G_0[4]; |
assign G_1[7]=G_0[7]&G_0[6]; |
assign G_1[9]=G_0[9]&G_0[8]; |
assign G_1[11]=G_0[11]&G_0[10]; |
assign G_1[13]=G_0[13]&G_0[12]; |
assign G_1[15]=G_0[15]&G_0[14]; |
assign G_1[0]=G_0[0]; |
assign G_1[2]=G_0[2]; |
assign G_1[4]=G_0[4]; |
assign G_1[6]=G_0[6]; |
assign G_1[8]=G_0[8]; |
assign G_1[10]=G_0[10]; |
assign G_1[12]=G_0[12]; |
assign G_1[14]=G_0[14]; |
|
//stage 2 |
wire [15:0]G_2; |
assign G_2[3]=G_1[3]&G_1[1]; |
assign G_2[5]=G_1[5]&G_1[3]; |
assign G_2[7]=G_1[7]&G_1[5]; |
assign G_2[9]=G_1[9]&G_1[7]; |
assign G_2[11]=G_1[11]&G_1[9]; |
assign G_2[13]=G_1[13]&G_1[11]; |
assign G_2[15]=G_1[15]&G_1[13]; |
assign G_2[0]=G_1[0]; |
assign G_2[2]=G_1[2]; |
assign G_2[1]=G_1[1]; |
assign G_2[4]=G_1[4]; |
assign G_2[6]=G_1[6]; |
assign G_2[8]=G_1[8]; |
assign G_2[10]=G_1[10]; |
assign G_2[12]=G_1[12]; |
assign G_2[14]=G_1[14]; |
|
//stage 3 |
wire [15:0]G_3; |
assign G_3[5]=G_2[5]&G_2[1]; |
assign G_3[7]=G_2[7]&G_2[3]; |
assign G_3[9]=G_2[9]&G_2[5]; |
assign G_3[11]=G_2[11]&G_2[7]; |
assign G_3[13]=G_2[13]&G_2[9]; |
assign G_3[15]=G_2[15]&G_2[11]; |
assign G_3[0]=G_2[0]; |
assign G_3[2]=G_2[2]; |
assign G_3[1]=G_2[1]; |
assign G_3[4]=G_2[4]; |
assign G_3[3]=G_2[3]; |
assign G_3[6]=G_2[6]; |
assign G_3[8]=G_2[8]; |
assign G_3[10]=G_2[10]; |
assign G_3[12]=G_2[12]; |
assign G_3[14]=G_2[14]; |
|
//stage 4 |
wire [15:0]G_4; |
assign G_4[9]=G_3[9]&G_3[1]; |
assign G_4[11]=G_3[11]&G_3[3]; |
assign G_4[13]=G_3[13]&G_3[5]; |
assign G_4[15]=G_3[15]&G_3[7]; |
assign G_4[0]=G_3[0]; |
assign G_4[2]=G_3[2]; |
assign G_4[1]=G_3[1]; |
assign G_4[4]=G_3[4]; |
assign G_4[3]=G_3[3]; |
assign G_4[6]=G_3[6]; |
assign G_4[5]=G_3[5]; |
assign G_4[8]=G_3[8]; |
assign G_4[7]=G_3[7]; |
assign G_4[10]=G_3[10]; |
assign G_4[12]=G_3[12]; |
assign G_4[14]=G_3[14]; |
|
//stage 5 |
wire [15:0]G_5; |
assign G_5[2]=G_4[2]&G_4[1]; |
assign G_5[4]=G_4[4]&G_4[3]; |
assign G_5[6]=G_4[6]&G_4[5]; |
assign G_5[8]=G_4[8]&G_4[7]; |
assign G_5[10]=G_4[10]&G_4[9]; |
assign G_5[12]=G_4[12]&G_4[11]; |
assign G_5[14]=G_4[14]&G_4[13]; |
assign G_5[1]=G_4[1]; |
assign G_5[3]=G_4[3]; |
assign G_5[5]=G_4[5]; |
assign G_5[7]=G_4[7]; |
assign G_5[9]=G_4[9]; |
assign G_5[11]=G_4[11]; |
assign G_5[13]=G_4[13]; |
assign G_5[15]=G_4[15]; |
assign G_5[0]=G_4[0]; |
|
//stage 6 |
assign o_result[0]=~i_operand[0]; |
assign o_result[15:1]=(G_5[14:0]&(~i_operand[15:1]))|((~G_5[14:0])&i_operand[15:1]); |
assign o_borrow=G_5[15]; |
|
endmodule |
/trunk/RTL/down_clocking_even.v
0,0 → 1,39
/*Author: Zhuxu |
m99a1@yahoo.cn |
Down clocking module |
Output clock frequency is the original frequency divided by an even number |
*/ |
module down_clocking_even( |
input i_clk, |
input i_rst, |
input [15:0]i_divisor, |
output o_clk |
); |
|
wire [15:0]divisor; |
wire borrow; |
|
minus_one minus_one_0( |
i_divisor, |
divisor, |
borrow |
); |
|
wire go; |
assign go=((i_divisor!=0)&&i_rst); |
reg [15:0]ct; |
reg clk; |
always@(posedge i_clk or i_rst) |
if(!i_rst)begin |
ct<=0; |
clk<=0; |
end |
else if(go)begin |
if(ct>=divisor)begin |
ct<=0; |
clk<=~clk; |
end |
else ct<=ct+1; |
end |
assign o_clk=go?clk:i_clk; |
endmodule |
/trunk/RTL/PWM.v
0,0 → 1,219
/*Author: Zhuxu |
m99a1@yahoo.cn |
Pulse Width Generators/timers with 16-bit main counter. |
Period or timers target number is controlled by register [15:0]period. |
Duty cycle is controlled by register [15:0]DC. |
Clock used for PWM signal generation can be switched between Wishbone Bus clock and external clock. It is down clocked first. |
o_pwm outputs PWM signal or timers interrupt. |
|
control register [7:0]ctrl: |
bit 0: When set, external clock is chosen for PWM/timer. When cleared, wb clock is used for PWM/timer. |
bit 1: When set, PWM is enabled. When cleared, timer is enabled. |
bit 2: When set, PWM/timer starts. When cleared, PWM/timer stops. |
bit 3: When set, timer runs continuously. When cleared, timer runs one time. |
bit 4: When set, o_pwm enabled. |
bit 5: timer interrupt bit When it is written with 0, interrupt request is cleared. |
bit 6: When set, a 16-bit external signal i_DC is used as duty cycle. When cleared, register DC is used. |
bit 7: When set, counter reset for PWM/timer, it's output and bit 5 will also be cleared. When changing from PWM mode to timer mode reset is needed before timer starts. |
*/ |
module PWM( |
//wishbone slave interface |
input i_wb_clk, |
input i_wb_rst, |
input i_wb_cyc, |
input i_wb_stb, |
input i_wb_we, |
input [15:0]i_wb_adr, |
input [15:0]i_wb_data, |
output [15:0]o_wb_data, |
output o_wb_ack, |
|
input i_extclk, |
input [15:0]i_DC, |
input i_valid_DC, |
output o_pwm |
|
); |
|
////////////////////control logic//////////////////////////// |
parameter adr_ctrl=0, |
adr_divisor=2, |
adr_period=4, |
adr_DC=6; |
reg [7:0]ctrl; |
reg [15:0]period; |
reg [15:0]DC; |
reg [15:0]divisor; //for down clocking. If(divisor==0)To=Ti; else To=Ti/divisor; |
integer i; |
wire write; |
assign write=i_wb_cyc&i_wb_stb&i_wb_we; |
assign o_wb_ack=i_wb_stb; |
always@(posedge i_wb_clk or posedge i_wb_rst) |
if(i_wb_rst)begin |
ctrl[4:0]<=0; |
ctrl[7:6]<=0; |
DC<=0; |
period<=0; |
divisor<=0; |
end |
else if(write)begin |
case(i_wb_adr) |
adr_ctrl:begin |
ctrl[4:0]<=i_wb_data[4:0]; |
ctrl[7:6]<=i_wb_data[7:6]; |
end |
adr_divisor:divisor<=i_wb_data; |
adr_period:period<=i_wb_data; |
adr_DC:DC<=i_wb_data; |
endcase |
end |
|
//interrupt bit control |
wire pwm; |
assign pwm=ctrl[1]; |
reg [1:0]state; |
reg clrint; //signal to pwm/timer logic |
wire ack_clrint; //signal from pwm/timer logic |
wire int; //signal from pwm/timer logic |
always@(posedge i_wb_clk or posedge i_wb_rst) |
if(i_wb_rst)begin |
ctrl[5]<=0; |
state<=0; |
clrint<=0; |
end |
else begin |
case(state) |
1:begin |
if(write)begin |
if(i_wb_data[7])begin |
ctrl[5]<=0; |
state<=0; |
end |
else if(!i_wb_data[5])begin |
ctrl[5]<=0; |
if(!pwm)begin |
clrint<=1; |
state<=2; |
end |
else state<=0; |
end |
end |
end |
2:if(ack_clrint)begin |
clrint<=0; |
state<=0; |
end |
default:begin |
if(!pwm)ctrl[5]<=int; |
if(int)state<=1; |
end |
endcase |
end |
|
|
/////////////////////////////////////////////////////////// |
|
//////down clocking for pwm/timer/////////////////// |
wire clk_source; |
wire eclk,oclk; |
assign clk_source=ctrl[0]?i_extclk:i_wb_clk; |
down_clocking_even down_clocking_even_0( |
clk_source, |
(!i_wb_rst), |
{1'b0,divisor[15:1]}, |
eclk |
); |
down_clocking_odd down_clocking_odd_0( |
clk_source, |
(!i_wb_rst), |
{1'b0,divisor[15:1]}, |
oclk |
); |
wire clk; |
assign clk=divisor[0]?oclk:eclk; |
/////////////////////////////////////////////////////// |
|
/////////////////main counter ////////////////////////// |
reg [15:0]ct; |
reg pts; //PWM signal or timer interrupt signal |
reg [15:0]extDC; |
wire [15:0]DC_1; |
assign DC_1=ctrl[6]?extDC:DC; //external or internal duty cycle toggle |
wire [15:0]period_1; |
assign period_1=(period==0)?0:(period-1); |
reg switch_ack_clrint; |
wire state_timer; |
assign state_timer=ctrl[3]; |
wire rst_ct; |
assign rst_ct=i_wb_rst|ctrl[7]; |
assign int=pwm?0:pts; |
assign ack_clrint=switch_ack_clrint?clrint:0; |
always@(posedge clk or posedge rst_ct) |
if(rst_ct)begin |
pts<=0; |
ct<=0; |
switch_ack_clrint<=0; |
extDC<=0; |
end |
else begin |
if(i_valid_DC)extDC<=i_DC; |
if(switch_ack_clrint&&(!clrint))switch_ack_clrint<=0; |
if(ctrl[2])begin |
case(pwm) |
1:begin |
if(ct>=period_1)ct<=0; |
else ct<=ct+1; |
|
if(ct<DC_1)pts<=1; |
else pts<=0; |
end |
0:begin |
|
case(state_timer) |
0:begin |
if(clrint)switch_ack_clrint<=1; |
if(ct>=period_1)begin |
if(clrint)begin |
pts<=0; |
ct<=0; |
end |
else pts<=1; |
|
end |
else ct<=ct+1; |
end |
1:begin |
if(ct>=period_1)begin |
pts<=1; |
ct<=0; |
end |
else begin |
if(clrint)begin |
switch_ack_clrint<=1; |
pts<=0; |
end |
ct<=ct+1; |
end |
end |
endcase |
end |
endcase |
end |
else if(clrint)begin |
switch_ack_clrint<=1; |
if(!pwm)begin |
pts<=0; |
ct<=0; |
end |
end |
end |
////////////////////////////////////////////////////////// |
|
assign o_pwm=ctrl[4]?pts:0; |
assign o_wb_data= i_wb_adr==adr_ctrl?{8'h0,ctrl}: |
i_wb_adr==adr_divisor?divisor: |
i_wb_adr==adr_period?period: |
i_wb_adr==adr_DC?DC:0; |
|
|
endmodule |
/trunk/RTL/down_clocking_odd.v
0,0 → 1,69
/*Author: Zhuxu |
m99a1@yahoo.cn |
Down clocking module |
Output clock frequency is the original frequency divided by an odd number |
*/ |
module down_clocking_odd( |
input i_clk, |
input i_rst, |
input [15:0]i_divisor, |
output o_clk |
); |
|
reg a,b; |
wire c; |
|
assign c=(~a)&(~b); |
wire [15:0]divisor; |
wire borrow; |
minus_one minus_one_0( |
i_divisor, |
divisor, |
borrow |
); |
|
wire go; |
assign go=((i_divisor!=0)&&i_rst); |
reg [15:0]ct_0; |
always@(posedge i_clk or negedge i_rst) |
if(!i_rst)begin |
a<=0; |
ct_0<=0; |
end |
else if(go)begin |
if(a)begin |
if(ct_0>=divisor)begin |
ct_0<=0; |
a<=0; |
end |
else ct_0<=ct_0+1; |
end |
else if(c)a<=c; |
end |
|
|
reg [15:0]ct_1; |
always@(negedge i_clk or negedge i_rst) |
if(!i_rst)begin |
b<=0; |
ct_1<=0; |
end |
else if(go)begin |
if(b)begin |
if(ct_1>=divisor)begin |
ct_1<=0; |
b<=0; |
end |
else ct_1<=ct_1+1; |
end |
else if(c)b<=c; |
end |
|
reg clk; |
always@(posedge c or negedge i_rst) |
if(!i_rst)clk<=0; |
else clk<=~clk; |
|
assign o_clk=go?clk:i_clk; |
|
endmodule |
/trunk/doc/PWM_um.doc
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
trunk/doc/PWM_um.doc
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/doc/PWM_UM.pdf
===================================================================
--- trunk/doc/PWM_UM.pdf (nonexistent)
+++ trunk/doc/PWM_UM.pdf (revision 2)
@@ -0,0 +1,1491 @@
+%PDF-1.4
+%
+364 0 obj
+<>
+endobj
+
+xref
+364 40
+0000000016 00000 n
+0000001621 00000 n
+0000001936 00000 n
+0000002121 00000 n
+0000002476 00000 n
+0000002503 00000 n
+0000002659 00000 n
+0000002799 00000 n
+0000003023 00000 n
+0000003126 00000 n
+0000003514 00000 n
+0000003784 00000 n
+0000003829 00000 n
+0000004616 00000 n
+0000005304 00000 n
+0000005438 00000 n
+0000006169 00000 n
+0000006314 00000 n
+0000006683 00000 n
+0000007357 00000 n
+0000008040 00000 n
+0000008711 00000 n
+0000009339 00000 n
+0000009682 00000 n
+0000009799 00000 n
+0000047389 00000 n
+0000047675 00000 n
+0000048175 00000 n
+0000048288 00000 n
+0000081777 00000 n
+0000082060 00000 n
+0000082433 00000 n
+0000095004 00000 n
+0000095059 00000 n
+0000095145 00000 n
+0000098901 00000 n
+0000099148 00000 n
+0000099355 00000 n
+0000001436 00000 n
+0000001118 00000 n
+trailer
+<<6A21DE5AD093C14486F148611DC98758>]/Prev 118189/XRefStm 1436>>
+startxref
+0
+%%EOF
+
+403 0 obj
+<>stream
+hb```b``b`c`` Ȁ @YSRc^Ú%ē jES~s۶
+@٢@LtTAAaPp13LØڙ&00E20DMx!i#c!5D;[Y`s30c {5 iQ/8<ȍ@<