clock_divider_counter.sv 7.28 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

////////////////////////////////////////////////////////////////////////////////
// Company:        Multitherman Laboratory @ DEIS - University of Bologna     //
//                    Viale Risorgimento 2 40136                              //
//                    Bologna - fax 0512093785 -                              //
//                                                                            //
// Engineer:       Antonio Pullini - pullinia@iis.ee.ethz.ch                  //
//                                                                            //
// Additional contributions by:                                               //
//                                                                            //
//                                                                            //
//                                                                            //
// Create Date:    13/02/2013                                                 //
// Design Name:    ULPSoC                                                     //
// Module Name:    clock_divider_counter                                      //
// Project Name:   ULPSoC                                                     //
// Language:       SystemVerilog                                              //
//                                                                            //
// Description:    clock_divider_counter                                      //
//                                                                            //
//                                                                            //
// Revision:                                                                  //
// Revision v0.1 - File Created                                               //
// Revision v0.2 - (19/03/2015)   clock_gating swapped in pulp_clock_gating   //
//                                                                            //
//                                                                            //
//                                                                            //
//                                                                            //
//                                                                            //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////


module clock_divider_counter
#(
    parameter BYPASS_INIT = 1,
    parameter DIV_INIT    = 'hFF
)
(
    input  logic       clk,
    input  logic       rstn,
    input  logic       test_mode,
    input  logic [7:0] clk_div,
    input  logic       clk_div_valid,
    output logic       clk_out
);

    logic [7:0]         counter;
    logic [7:0]         counter_next;
    logic [7:0]         clk_cnt;
    logic               en1;
    logic               en2;

    logic               is_odd;

    logic               div1;
    logic               div2;
    logic               div2_neg_sync;

    logic [7:0]         clk_cnt_odd;
    logic [7:0]         clk_cnt_odd_incr;
    logic [7:0]         clk_cnt_even;
    logic [7:0]         clk_cnt_en2;

    logic               bypass;

    logic               clk_out_gen;
    logic               clk_div_valid_reg;

    logic               clk_inv_test;
    logic               clk_inv;

    //        assign clk_cnt_odd_incr = clk_div + 1;
    //        assign clk_cnt_odd  = {1'b0,clk_cnt_odd_incr[7:1]}; //if odd divider than clk_cnt = (clk_div+1)/2
    assign clk_cnt_odd  = clk_div - 8'h1; //if odd divider than clk_cnt = clk_div - 1
    assign clk_cnt_even = (clk_div == 8'h2) ? 8'h0 : ({1'b0,clk_div[7:1]} - 8'h1);   //if even divider than clk_cnt = clk_div/2
    assign clk_cnt_en2  = {1'b0,clk_cnt[7:1]} + 8'h1;

    always_comb
    begin
        if (counter == 'h0)
            en1 = 1'b1;
        else
            en1 = 1'b0;

        if (clk_div_valid)
            counter_next = 'h0;
        else if (counter == clk_cnt)
                counter_next = 'h0;
             else
                counter_next = counter + 1;

        if (clk_div_valid)
            en2 = 1'b0;
        else if (counter == clk_cnt_en2)
                en2 = 1'b1;
             else
                en2 = 1'b0;
    end

   always_ff @(posedge clk, negedge rstn)
   begin
        if (~rstn)
        begin
             counter            <=  'h0;
             div1               <= 1'b0;
             bypass             <= BYPASS_INIT;
             clk_cnt            <= DIV_INIT;
             is_odd             <= 1'b0;
             clk_div_valid_reg  <= 1'b0;
        end
        else
        begin
              if (!bypass)
                  counter <= counter_next;

              clk_div_valid_reg <= clk_div_valid;
              if (clk_div_valid)
              begin
                if ((clk_div == 8'h0) || (clk_div == 8'h1))
                  begin
                      bypass <= 1'b1;
                      clk_cnt <= 'h0;
                      is_odd  <= 1'b0;
                  end
                else
                  begin
                      bypass <= 1'b0;
                      if (clk_div[0])
                        begin
                          is_odd  <= 1'b1;
                          clk_cnt <= clk_cnt_odd;
                        end
                      else
                        begin
                          is_odd  <= 1'b0;
                          clk_cnt <= clk_cnt_even;
                        end
                  end
                div1 <= 1'b0;
              end
              else
              begin
                if (en1 && !bypass)
                  div1 <= ~div1;
              end
        end
    end

    pulp_clock_inverter clk_inv_i
    (
        .clk_i(clk),
        .clk_o(clk_inv)
    );

`ifndef PULP_FPGA_EMUL
 `ifdef PULP_DFT
   pulp_clock_mux2 clk_muxinv_i
     (
      .clk0_i(clk_inv),
      .clk1_i(clk),
      .clk_sel_i(test_mode),
      .clk_o(clk_inv_test)
      );
 `else
   assign clk_inv_test = clk_inv;
 `endif
`else
   assign clk_inv_test = clk_inv;
`endif

    always_ff @(posedge clk_inv_test or negedge rstn)
    begin
        if (!rstn)
        begin
            div2    <= 1'b0;
        end
        else
        begin
            if (clk_div_valid_reg)
                div2 <= 1'b0;
            else if (en2 && is_odd && !bypass)
                    div2 <= ~div2;
        end
    end // always_ff @ (posedge clk_inv_test or negedge rstn)

    pulp_clock_xor2 clock_xor_i
    (
        .clk_o(clk_out_gen),
        .clk0_i(div1),
        .clk1_i(div2)
    );

    pulp_clock_mux2 clk_mux_i
    (
        .clk0_i(clk_out_gen),
        .clk1_i(clk),
        .clk_sel_i(bypass || test_mode),
        .clk_o(clk_out)
    );

endmodule