| // Copyright 2020 Thomas Parry |
| // |
| // SOME LICENSE |
| // |
| // Fractional N Divider |
| // A fractional N divider using MASH modulation. |
| // |
| // The 'data_in' value divided by the modulus will be created |
| // in a quantisesd number stream with high passed noise shaping |
| // characteristic. |
| // |
| |
| module fractional_n_divider |
| # ( |
| parameter WIDTH_INTEGER = 10, |
| parameter WIDTH_MODULUS = 16, |
| parameter ORDER = 3, |
| parameter DATA_WIDTH = WIDTH_INTEGER+WIDTH_MODULUS |
| ) ( |
| input rst, |
| |
| // input and output frequencies |
| input input_frequency, |
| output output_frequency, |
| |
| // a manual interface for data |
| input [DATA_WIDTH-1:0] divide_value, |
| input [1:0] dither_select, |
| |
| // test outputs |
| output dither_output |
| ); |
| |
| // internal signals |
| wire [WIDTH_INTEGER-1:0] integer_value; |
| wire [WIDTH_MODULUS-1:0] fractional_value; |
| wire signed [ORDER-1:0] mash_output; |
| wire [WIDTH_INTEGER-1:0] count_target; |
| reg [WIDTH_INTEGER-1:0] count; |
| reg output_state; |
| reg dither; |
| reg dither_en_lfsr; |
| reg dither_in_lfsr; |
| reg dither_en_trng; |
| wire dither_out_lfsr; |
| wire dither_out_trng; |
| |
| |
| // split the divider values |
| assign integer_value = divide_value[DATA_WIDTH-1:WIDTH_MODULUS]; |
| assign fractional_value = divide_value[WIDTH_MODULUS-1:0] + dither; |
| |
| // instantiate the MASH modulator |
| mash_mod # ( |
| .WIDTH_MODULUS(WIDTH_MODULUS), |
| .ORDER(ORDER) |
| ) mash_mod_inst ( |
| .clk(output_frequency), |
| .rst(rst), |
| .data_in(fractional_value), |
| .data_out(mash_output) |
| ); |
| |
| // combine the integer and fractional components |
| assign count_target = $unsigned($signed(integer_value) + {{(WIDTH_INTEGER-ORDER){mash_output[ORDER-1]}}, mash_output}); |
| |
| // count until the target is met and then output a pulse |
| always @(posedge rst, posedge input_frequency) begin |
| if (rst) begin |
| count = 0; |
| output_state = 0; |
| end |
| else begin |
| if (count == count_target) begin |
| count = 0; |
| output_state = 1; |
| end |
| else begin |
| count = count + 1; |
| output_state = 0; |
| end |
| end |
| end |
| assign output_frequency = output_state; |
| |
| // LFSR based dither |
| lfsr_fib lfsr_fib_inst ( |
| .i_clk(output_frequency), |
| .i_reset(rst), |
| .i_ce(dither_en_lfsr), |
| .i_in(dither_in_lfsr), |
| .o_bit(dither_out_lfsr) |
| ); |
| |
| // TRNG based dither |
| trng trng_inst ( |
| .clk(output_frequency), |
| .rst(rst), |
| .stop(!dither_en_trng), |
| .random(dither_out_trng) |
| ); |
| |
| // select the dither source |
| always @(posedge output_frequency) begin |
| case(dither_select) |
| 2'b00 :begin |
| dither <= 0; |
| dither_en_lfsr <= 0; |
| dither_in_lfsr <= 0; |
| dither_en_trng <= 0; |
| end |
| |
| 2'b01 : begin |
| dither <= dither_out_trng; |
| dither_en_lfsr <= 0; |
| dither_in_lfsr <= 0; |
| dither_en_trng <= 1; |
| end |
| |
| 2'b10 : begin |
| dither <= dither_out_lfsr; |
| dither_en_lfsr <= 1; |
| dither_in_lfsr <= 0; |
| dither_en_trng <= 0; |
| end |
| |
| 2'b11 : begin |
| dither <= dither_out_lfsr; |
| dither_en_lfsr <= 1; |
| dither_in_lfsr <= 1; |
| dither_en_trng <= 0; |
| end |
| |
| default : begin |
| dither <= 0; |
| dither_en_lfsr <= 0; |
| dither_in_lfsr <= 0; |
| dither_en_trng <= 0; |
| end |
| endcase |
| end |
| |
| assign dither_output = dither; |
| |
| endmodule |