OpenCores
URL https://opencores.org/ocsvn/neopixel_fpga/neopixel_fpga/trunk

Subversion Repositories neopixel_fpga

[/] [neopixel_fpga/] [trunk/] [rtl/] [ws2812_sequence.v] - Rev 3

Compare with Previous | Blame | View Log

/*
 *  FpgaNeoPixel - A spi to ws2812 machine
 *
 *  Copyright (C) 2020  Hirosh Dabui <hirosh@dabui.de>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */
`ifndef WS2812_DATA_SEQUENCE_H
`define WS2812_DATA_SEQUENCE_H
module ws2812_sequence(clk, resetn, enable, din, dout, done);
  /*
   * e.g. 12 MHz = 83,33ns = 0.083us cycles
   *
   * 0,45 us => 450 ns
   *
   * T0H = 0,4 us = 400 ns ~ cycles * 5 = 416.65 ns
   * T0L = 0.85 us = 850 ns ~ cycles * 10 = 833.3 ns
   *
   * T0H + T0L = 1250 ns ~ 1249.95 ns
   *
   * T1H = 0.8us = 800 ns ~ cycles * 10 = 833.3 ns
   * T1L = 0.45us = 450 ns ~ cycles * 5 = 416.65 ns
   *
   * T0H + T0L = 1250 ns ~ 1249.95 ns
   *
   * RES above 50 us = 50000ns ~ cycles * 610 = 50831 ns
   * +/- 150 ns
   *
   * with 12 MHz = 1249.95 = +/- 0.05 ns
   *
   *  ______
   * | T0H  |__T0L___|
   *
   *  ______
   * | T1H  |__T1L___|
   *
   * |____Treset ____|
   *
   */
  localparam GRB_WIDTH = 24;
 
  parameter SYSTEM_CLOCK_FREQ = 12_000_000;
  parameter T0H_TIME = 0.4e-6; // 400 ns
  parameter T0L_TIME = 0.85e-6; // 850 ns
  localparam  CLK_HZ = $itor(SYSTEM_CLOCK_FREQ);
  localparam CLK_CYCLE = (1/CLK_HZ);
 
  localparam T0H_CYCLES = $rtoi((T0H_TIME)/CLK_CYCLE+0.5);//5;
  localparam T0L_CYCLES = $rtoi((T0L_TIME)/CLK_CYCLE+0.5);//10;
 
  localparam T1H_CYCLES = T0L_CYCLES;
  localparam T1L_CYCLES = T0H_CYCLES;
 
  localparam SEQUENCE_CYCLE = T0H_CYCLES + T0L_CYCLES;
 
  localparam TRESET_CYCLES = $rtoi((50e-6)/CLK_CYCLE+0.5);//610; // ~ 50us
 
  localparam CYCLE_BITS = $clog2(SEQUENCE_CYCLE);
 
  input clk;
  input resetn;
  input enable;
 
  /* |G7|...|G0|R7|...|R0|B7|..|B0| */
  input wire [GRB_WIDTH -1:0] din;
 
  output reg dout = 1'b0;
  output reg done = 1'b0;
 
  reg [GRB_WIDTH - 1:0] data_in = 0;
  reg [CYCLE_BITS-1:0] dout_cycle_cnt = 0;
  reg [$clog2(GRB_WIDTH-1):0] dout_bit_cycle_cnt = 0;
  reg dbit = 0;
 
 
  // states
  localparam S_RESET = 0;
  localparam S_IDLE = 1;
  localparam S_SEQUENCE_OUT = 2;
 
  localparam S_TXH_DATA_SEQUENCE = 3;
  localparam S_TXL_DATA_SEQUENCE = 4;
 
  reg [2:0] state = S_RESET;
 
  initial begin
    $display("CLK_HZ: ", CLK_HZ);
    $display("CLK_CYCLE: ", CLK_CYCLE);
    $display("T0H_CYCLES: ", T0H_CYCLES);
    $display("T0L_CYCLES: ", T0L_CYCLES);
    $display("SEQUENCE_CYCLE: ", SEQUENCE_CYCLE);
    $display("CYCLE_BITS: ", CYCLE_BITS);
    $display("$clog2(GRB_WIDTH): ", $clog2(GRB_WIDTH));
    $display("TRESET_CYCLES: ", TRESET_CYCLES);
  end
 
  always @(posedge clk) begin
    if (~resetn) begin
      done <= 0;
      dout <= 0;
      state <= S_RESET;
    end else begin
      case (state)
 
        S_RESET: begin
          state <= S_IDLE;
        end
 
        S_IDLE: begin
          done <= 0;
          dout_bit_cycle_cnt <= 0;
          dout_cycle_cnt <= 0;
          dout <= 0;
          if (enable) begin
            data_in <= din;
            state <= S_SEQUENCE_OUT;
          end else
            state <= S_IDLE;
        end
 
      S_SEQUENCE_OUT: begin
        dout_bit_cycle_cnt <= dout_bit_cycle_cnt + 1;
        if (dout_bit_cycle_cnt == GRB_WIDTH) begin
          done <= 1;
          state <= S_IDLE;
        end else begin
          dout_cycle_cnt <= 1;
          data_in <= {data_in[GRB_WIDTH -2: 0], 1'b0};
          dbit <= data_in[GRB_WIDTH -1];
          state <= S_TXH_DATA_SEQUENCE;
        end
      end
 
      S_TXH_DATA_SEQUENCE: begin
        dout_cycle_cnt <= dout_cycle_cnt + 1;
        if (dout_cycle_cnt < (dbit ? T1H_CYCLES[CYCLE_BITS-1:0] : T0H_CYCLES[CYCLE_BITS-1:0])) begin
          dout <= 1;
          state <= S_TXH_DATA_SEQUENCE;
        end
        else begin
          dout_cycle_cnt <= 1;
          state <= S_TXL_DATA_SEQUENCE;
        end
      end
 
      S_TXL_DATA_SEQUENCE: begin
        dout_cycle_cnt <= dout_cycle_cnt + 1;
        if ((dout_cycle_cnt < (dbit ? T1L_CYCLES[CYCLE_BITS-1:0] -1: T0L_CYCLES[CYCLE_BITS-1:0] -1))) begin
          dout <= 0;
          state <= S_TXL_DATA_SEQUENCE;
        end
        else
          state <= S_SEQUENCE_OUT;
      end
 
      // fall-through reset
      default: begin
        state <= S_RESET;
      end
    endcase
  end
end
 
endmodule
`endif
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.