├── .gitignore ├── Makefile ├── tb.v ├── util.v └── ws2811.v /.gitignore: -------------------------------------------------------------------------------- 1 | out.* -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for icarus verilog 2 | 3 | # Automatically glob all .v files 4 | SOURCES = $(wildcard *.v) 5 | 6 | # By default, compile, simulate, and open waveform viewer 7 | default: display 8 | 9 | .PHONY: compile 10 | compile: out.vvp 11 | 12 | .PHONY: simulate 13 | simulate: out.vcd 14 | 15 | # View waveforms in GTKWave 16 | .PHONY: display 17 | display: out.vcd 18 | @touch out.gtkw 19 | gtkwave -A -f out.vcd 20 | 21 | # Compile verilog to VVP file 22 | out.vvp: $(SOURCES) 23 | iverilog -o out.vvp $(SOURCES) 24 | 25 | # Run the simulation 26 | out.vcd: out.vvp 27 | vvp out.vvp 28 | 29 | # Delete temporary files 30 | clean: 31 | rm -f out.vvp 32 | rm -f out.vcd 33 | rm -f out.gtkw 34 | -------------------------------------------------------------------------------- /tb.v: -------------------------------------------------------------------------------- 1 | `timescale 10ns/1ns 2 | 3 | module tb; 4 | reg clk; 5 | reg reset; 6 | 7 | wire [2:0] address; 8 | reg [7:0] red; 9 | reg [7:0] green; 10 | reg [7:0] blue; 11 | 12 | wire DO; 13 | 14 | ws2811 15 | #( 16 | .NUM_LEDS(8), 17 | .SYSTEM_CLOCK(100000000) 18 | ) driver 19 | ( 20 | .clk(clk), 21 | .reset(reset), 22 | 23 | .address(address), 24 | .red_in(red), 25 | .green_in(green), 26 | .blue_in(blue), 27 | 28 | .DO(DO) 29 | ); 30 | 31 | 32 | initial begin 33 | $dumpfile("out.vcd"); 34 | $dumpvars(0, tb); 35 | 36 | clk = 0; 37 | reset = 1; 38 | 39 | red = 8'hFF; 40 | green = 8'hAA; 41 | blue = 8'h00; 42 | 43 | #50 reset = 0; 44 | #1000000 $finish(); 45 | end 46 | 47 | always clk = #0.5 ~clk; 48 | 49 | endmodule 50 | -------------------------------------------------------------------------------- /util.v: -------------------------------------------------------------------------------- 1 | `define log2(n) ((n) <= (1<<0) ? 0 : (n) <= (1<< 1) ? 1 :\ 2 | (n) <= (1<<2) ? 2 : (n) <= (1<< 3) ? 3 :\ 3 | (n) <= (1<<4) ? 4 : (n) <= (1<< 5) ? 5 :\ 4 | (n) <= (1<<6) ? 6 : (n) <= (1<< 7) ? 7 :\ 5 | (n) <= (1<<8) ? 8 : (n) <= (1<< 9) ? 9 :\ 6 | (n) <= (1<<10) ? 10 : (n) <= (1<<11) ? 11 :\ 7 | (n) <= (1<<12) ? 12 : (n) <= (1<<13) ? 13 :\ 8 | (n) <= (1<<14) ? 14 : (n) <= (1<<15) ? 15 :\ 9 | (n) <= (1<<16) ? 16 : (n) <= (1<<17) ? 17 :\ 10 | (n) <= (1<<18) ? 18 : (n) <= (1<<19) ? 19 :\ 11 | (n) <= (1<<20) ? 20 : (n) <= (1<<21) ? 21 :\ 12 | (n) <= (1<<22) ? 22 : (n) <= (1<<23) ? 23 :\ 13 | (n) <= (1<<24) ? 24 : (n) <= (1<<25) ? 25 :\ 14 | (n) <= (1<<26) ? 26 : (n) <= (1<<27) ? 27 :\ 15 | (n) <= (1<<28) ? 28 : (n) <= (1<<29) ? 29 :\ 16 | (n) <= (1<<30) ? 30 : (n) <= (1<<31) ? 31 : 32) 17 | -------------------------------------------------------------------------------- /ws2811.v: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////// 2 | // Driver for WS2811-based LED strips // 3 | //////////////////////////////////////// 4 | 5 | module ws2811 6 | #( 7 | parameter NUM_LEDS = 4, // The number of LEDS in the chain 8 | parameter SYSTEM_CLOCK = 100_000_000 // The frequency of the input clock signal, in Hz. This value must be correct in order to have correct timing for the WS2811 protocol. 9 | ) 10 | ( 11 | input clk, // Clock input. 12 | input reset, // Resets the internal state of the driver 13 | 14 | ///////////////////// 15 | // Control signals // 16 | ///////////////////// 17 | output data_request, // This signal is asserted one cycle before red_in, green_in, and blue_in are sampled. 18 | output new_address, // This signal is asserted whenever the address signal is updated to its new value. 19 | output reg [LED_ADDRESS_WIDTH-1:0] address, // The current LED number. This signal is incremented to the next value two cycles after the last time data_request was asserted. 20 | 21 | input [7:0] red_in, // 8-bit red data 22 | input [7:0] green_in, // 8-bit green data 23 | input [7:0] blue_in, // 8-bit blue data 24 | 25 | //////////////////// 26 | // External ports // 27 | //////////////////// 28 | output reg DO // Signal to send to WS2811 chain. 29 | ); 30 | 31 | function integer log2; 32 | input integer value; 33 | begin 34 | value = value-1; 35 | for (log2=0; value>0; log2=log2+1) 36 | value = value>>1; 37 | end 38 | endfunction 39 | 40 | localparam integer LED_ADDRESS_WIDTH = log2(NUM_LEDS); // Number of bits to use for address input 41 | 42 | ///////////////////////////////////////////////////////////// 43 | // Timing parameters for the WS2811 // 44 | // The LEDs are reset by driving D0 low for at least 50us. // 45 | // Data is transmitted using a 800kHz signal. // 46 | // A '1' is 50% duty cycle, a '0' is 20% duty cycle. // 47 | ///////////////////////////////////////////////////////////// 48 | localparam integer CYCLE_COUNT = SYSTEM_CLOCK / 800000; 49 | localparam integer H0_CYCLE_COUNT = 0.32 * CYCLE_COUNT; 50 | localparam integer H1_CYCLE_COUNT = 0.64 * CYCLE_COUNT; 51 | localparam integer CLOCK_DIV_WIDTH = log2(CYCLE_COUNT); 52 | 53 | localparam integer RESET_COUNT = 100 * CYCLE_COUNT; 54 | localparam integer RESET_COUNTER_WIDTH = log2(RESET_COUNT); 55 | 56 | reg [CLOCK_DIV_WIDTH-1:0] clock_div; // Clock divider for a cycle 57 | reg [RESET_COUNTER_WIDTH-1:0] reset_counter; // Counter for a reset cycle 58 | 59 | localparam STATE_RESET = 3'd0; 60 | localparam STATE_LATCH = 3'd1; 61 | localparam STATE_PRE = 3'd2; 62 | localparam STATE_TRANSMIT = 3'd3; 63 | localparam STATE_POST = 3'd4; 64 | reg [2:0] state; // FSM state 65 | 66 | localparam COLOR_G = 2'd0; 67 | localparam COLOR_R = 2'd1; 68 | localparam COLOR_B = 2'd2; 69 | reg [1:0] color; // Current color being transferred 70 | 71 | reg [7:0] red; 72 | reg [7:0] green; 73 | reg [7:0] blue; 74 | 75 | reg [7:0] current_byte; // Current byte to send 76 | reg [2:0] current_bit; // Current bit index to send 77 | 78 | wire reset_almost_done; 79 | wire led_almost_done; 80 | 81 | assign reset_almost_done = (state == STATE_RESET) && (reset_counter == RESET_COUNT-1); 82 | assign led_almost_done = (state == STATE_POST) && (color == COLOR_B) && (current_bit == 0) && (address != 0); 83 | 84 | assign data_request = reset_almost_done || led_almost_done; 85 | assign new_address = (state == STATE_PRE) && (current_bit == 7); 86 | 87 | always @ (posedge clk) begin 88 | if (reset) begin 89 | address <= 0; 90 | state <= STATE_RESET; 91 | DO <= 0; 92 | reset_counter <= 0; 93 | color <= COLOR_G; 94 | current_bit <= 7; 95 | end 96 | else begin 97 | case (state) 98 | STATE_RESET: begin 99 | // De-assert DO, and wait for 75 us. 100 | DO <= 0; 101 | if (reset_counter == RESET_COUNT-1) begin 102 | reset_counter <= 0; 103 | state <= STATE_LATCH; 104 | end 105 | else begin 106 | reset_counter <= reset_counter + 1; 107 | end 108 | end // case: STATE_RESET 109 | STATE_LATCH: begin 110 | // Latch the input 111 | red <= red_in; 112 | // green <= green_in; 113 | blue <= blue_in; 114 | 115 | // Setup the new address 116 | address <= address + 1; 117 | 118 | // Start sending green 119 | color <= COLOR_G; 120 | current_byte <= green_in; 121 | current_bit <= 7; 122 | 123 | state <= STATE_PRE; 124 | end 125 | STATE_PRE: begin 126 | // Assert DO, start clock divider counter 127 | clock_div <= 0; 128 | DO <= 1; 129 | state <= STATE_TRANSMIT; 130 | end 131 | STATE_TRANSMIT: begin 132 | // De-assert DO after a certain amount of time, depending on if you're transmitting a 1 or 0. 133 | if (current_byte[7] == 0 && clock_div >= H0_CYCLE_COUNT) begin 134 | DO <= 0; 135 | end 136 | else if (current_byte[7] == 1 && clock_div >= H1_CYCLE_COUNT) begin 137 | DO <= 0; 138 | end 139 | // Advance cycle counter 140 | if (clock_div == CYCLE_COUNT-1) begin 141 | state <= STATE_POST; 142 | end 143 | else begin 144 | clock_div <= clock_div + 1; 145 | end 146 | end 147 | STATE_POST: begin 148 | if (current_bit != 0) begin 149 | // Start sending next bit of data 150 | current_byte <= {current_byte[6:0], 1'b0}; 151 | case (current_bit) 152 | 7: current_bit <= 6; 153 | 6: current_bit <= 5; 154 | 5: current_bit <= 4; 155 | 4: current_bit <= 3; 156 | 3: current_bit <= 2; 157 | 2: current_bit <= 1; 158 | 1: current_bit <= 0; 159 | endcase 160 | state <= STATE_PRE; 161 | end 162 | else begin 163 | // Advance to the next color. If we were on blue, advance to the next LED 164 | case (color) 165 | COLOR_G: begin 166 | color <= COLOR_R; 167 | current_byte <= red; 168 | current_bit <= 7; 169 | state <= STATE_PRE; 170 | end 171 | COLOR_R: begin 172 | color <= COLOR_B; 173 | current_byte <= blue; 174 | current_bit <= 7; 175 | state <= STATE_PRE; 176 | end 177 | COLOR_B: begin 178 | // If we were on the last LED, send out reset pulse 179 | if (address == 0) begin 180 | state <= STATE_RESET; 181 | end 182 | else begin 183 | state <= STATE_LATCH; 184 | end 185 | end 186 | endcase // case (color) 187 | end 188 | end 189 | endcase 190 | end 191 | end 192 | 193 | endmodule 194 | --------------------------------------------------------------------------------