diff --git a/pdm_modulator.sv b/pdm_modulator.sv new file mode 100644 index 0000000..80df934 --- /dev/null +++ b/pdm_modulator.sv @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// pdm_modulator.sv +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Pulse density modulation (PDM) generator module +// +// - expecting 8-bit control signal input by default +// - system clock is 100 MHz by default +// +// - see also pwm_modulator.sv for pulse width modulation generator + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +pdm_modulator #( + .PDM_PERIOD_DIV( 9 ) + .MOD_WIDTH( 8 ) // from 0 to 255 +) pdm1 ( + .clk( clk ), + .nrst( nrst ), + + .control( ), + .pdm_out( ), + + .start_strobe( ), + .busy( ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + +module pdm_modulator #( parameter + CLK_HZ = 100_000_000, + PDM_PERIOD_DIV = 16, // must be > MOD_WIDTH + PDM_MIN_PERIOD_HZ = CLK_HZ / (2**PDM_PERIOD_DIV) * (0+2), // two PDM clock cycles + PDM_MAX_PERIOD_HZ = CLK_HZ / (2**PDM_PERIOD_DIV) * (256+2), + + MOD_WIDTH = 8 // modulation bitness +)( + input clk, // system clock + input nrst, // negative reset + + input [MOD_WIDTH-1:0] mod_setpoint, // modulation setpoint + output pdm_out, // active HIGH output + + // status outputs + output start_strobe, // period start strobe + output busy // busy output +); + + +// period generator +logic [31:0] div_clk; +clk_divider #( + .WIDTH( 32 ) +) cd1 ( + .clk( clk ), + .nrst( nrst ), + .ena( 1'b1 ), + .out( div_clk[31:0] ) +); + + +// pulse generator +pulse_gen #( + .CNTR_WIDTH( MOD_WIDTH+1 ) +) pg1 ( + .clk( div_clk[(PDM_PERIOD_DIV-1)-MOD_WIDTH] ), + .nrst( nrst ), + + .start( 1'b1 ), + .cntr_max( mod_setpoint[MOD_WIDTH-1:0]+2 ), + .cntr_low( 1 ), + + .pulse_out( pdm_out ), + + .start_strobe( start_strobe ), + .busy( busy ) +); + + +endmodule + diff --git a/pdm_modulator_tb.sv b/pdm_modulator_tb.sv new file mode 100644 index 0000000..0fc4886 --- /dev/null +++ b/pdm_modulator_tb.sv @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// pdm_modulator_tb.sv +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// testbench for pdm_modulator.sv module + + +`timescale 1ns / 1ps + +module pdm_modulator_tb(); + +logic clk200; +initial begin + #0 clk200 = 1'b0; + forever + #2.5 clk200 = ~clk200; +end + +// external device "asynchronous" clock +logic clk33; +initial begin + #0 clk33 = 1'b0; + forever + #15.151 clk33 = ~clk33; +end + +logic rst; +initial begin + #0 rst = 1'b0; + #10.2 rst = 1'b1; + #5 rst = 1'b0; + //#10000; + forever begin + #9985 rst = ~rst; + #5 rst = ~rst; + end +end + +logic nrst; +assign nrst = ~rst; + +logic rst_once; +initial begin + #0 rst_once = 1'b0; + #10.2 rst_once = 1'b1; + #5 rst_once = 1'b0; +end + +logic nrst_once; +assign nrst_once = ~rst_once; + +logic [31:0] DerivedClocks; +clk_divider #( + .WIDTH( 32 ) +) cd1 ( + .clk( clk200 ), + .nrst( nrst_once ), + .ena( 1'b1 ), + .out( DerivedClocks[31:0] ) +); + +logic [31:0] E_DerivedClocks; +edge_detect ed1[31:0] ( + .clk( {32{clk200}} ), + .nrst( {32{nrst_once}} ), + .in( DerivedClocks[31:0] ), + .rising( E_DerivedClocks[31:0] ), + .falling( ), + .both( ) +); + +logic [31:0] RandomNumber1; +c_rand rng1 ( + .clk( clk200 ), + .rst( 1'b0 ), + .reseed( rst_once ), + .seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 1) ), + .out( RandomNumber1[15:0] ) +); + +c_rand rng2 ( + .clk( clk200 ), + .rst( 1'b0 ), + .reseed( rst_once ), + .seed_val( DerivedClocks[31:0] ^ (DerivedClocks[31:0] << 2) ), + .out( RandomNumber1[31:16] ) +); + +logic start; +initial begin + #0 start = 1'b0; + #100 start = 1'b1; + #20 start = 1'b0; +end + +// Modules under test ========================================================== + +localparam MOD_WIDTH = 5; + +logic [MOD_WIDTH-1:0] sp = '0; +logic [31:0][MOD_WIDTH-1:0] sin_table = +{ 5'd16, 5'd19, 5'd22, 5'd25, 5'd27, 5'd29, 5'd31, 5'd31, + 5'd31, 5'd31, 5'd30, 5'd28, 5'd26, 5'd23, 5'd20, 5'd17, + 5'd14, 5'd11, 5'd8, 5'd5, 5'd3, 5'd1, 5'd0, 5'd0, + 5'd0, 5'd0, 5'd2, 5'd4, 5'd6, 5'd9, 5'd12, 5'd15}; + + +always_ff @(posedge clk200) begin + if( ~nrst_once ) begin + sp[MOD_WIDTH-1:0] <= '0; + end else begin + if( E_DerivedClocks[3] ) begin + sp[MOD_WIDTH-1:0] <= sp[MOD_WIDTH-1:0] + 1'b1; + end + end +end + +pdm_modulator #( + .PDM_PERIOD_DIV( MOD_WIDTH+1 ), // MOD_WIDTH+1 is a minimum + .MOD_WIDTH( MOD_WIDTH ) +) pdm1 ( + .clk( clk200 ), + .nrst( nrst_once ), + + .mod_setpoint( sin_table[sp[MOD_WIDTH-1:0]][MOD_WIDTH-1:0] ), + .pdm_out( ), + + .start_strobe( ), + .busy( ) +); + + +endmodule diff --git a/pulse_gen_tb.sv b/pulse_gen_tb.sv index 226b1d0..15ae61e 100644 --- a/pulse_gen_tb.sv +++ b/pulse_gen_tb.sv @@ -108,7 +108,7 @@ end .cntr_max( 15 ), .cntr_low( 0 ), - .out( ), + .pulse_out( ), .busy( ) ); */ @@ -124,7 +124,7 @@ pulse_gen #( .cntr_max( 16 ), .cntr_low( {4'b0,RandomNumber1[3:0]} ), - .out( ), + .pulse_out( ), .busy( ) ); @@ -163,7 +163,7 @@ end .cntr_max( 15 ), .cntr_low( {4'b0,in_high_width[3:0]} ), - .out( out ), + .pulse_out( out ), .busy( ) );*/ diff --git a/pwm_gen.sv b/pwm_modulator.sv similarity index 88% rename from pwm_gen.sv rename to pwm_modulator.sv index d2549bb..c14279e 100644 --- a/pwm_gen.sv +++ b/pwm_modulator.sv @@ -1,22 +1,22 @@ //------------------------------------------------------------------------------ -// pwm_gen.sv +// pwm_modulator.sv // Konstantin Pavlov, pavlovconst@gmail.com //------------------------------------------------------------------------------ // INFO ------------------------------------------------------------------------ -// PWM generator module +// Pulse width modulation (PWM) generator module // -// - expecting 8-bit control signal input +// - expecting 8-bit control signal input by default // - system clock is 100 MHz by default // - PWM clock is 1.5KHz by default +// +// - see also pdm_modulator.sv for pulse density modulation generator /* --- INSTANTIATION TEMPLATE BEGIN --- -pwm_gen #( - .CLK_HZ( 100_000_000 ), // 100 MHz +pwm_modulator #( .PWM_PERIOD_DIV( 16 ) // 100MHz/2^16= ~1.526 KHz - .MOD_WIDTH( 8 ) // from 0 to 255 ) pwm1 ( .clk( clk ), @@ -31,7 +31,7 @@ pwm_gen #( --- INSTANTIATION TEMPLATE END ---*/ -module pwm_gen #( parameter +module pwm_modulator #( parameter CLK_HZ = 100_000_000, PWM_PERIOD_DIV = 16, // must be > MOD_WIDTH PWM_PERIOD_HZ = CLK_HZ / (2**PWM_PERIOD_DIV), diff --git a/pwm_gen_tb.sv b/pwm_modulator_tb.sv similarity index 95% rename from pwm_gen_tb.sv rename to pwm_modulator_tb.sv index f581b3f..9742d61 100644 --- a/pwm_gen_tb.sv +++ b/pwm_modulator_tb.sv @@ -1,15 +1,15 @@ //------------------------------------------------------------------------------ -// pwm_gen_tb.sv +// pwm_modulator_tb.sv // Konstantin Pavlov, pavlovconst@gmail.com //------------------------------------------------------------------------------ // INFO ------------------------------------------------------------------------ -// testbench for pwm_gen.sv module +// testbench for pwm_modulator.sv module `timescale 1ns / 1ps -module pwm_gen_tb(); +module pwm_modulator_tb(); logic clk200; initial begin @@ -117,8 +117,7 @@ always_ff @(posedge clk200) begin end end -pwm_gen #( - .CLK_HZ( 200_000_000 ), +pwm_modulator #( .PWM_PERIOD_DIV( MOD_WIDTH+1 ), // MOD_WIDTH+1 is a minimum .MOD_WIDTH( MOD_WIDTH ) ) pwm1 (