eris2206

Documentation: http://frombelow.net/projects/eris2206/
Clone: git clone https://git.frombelow.net/eris2206.git
Log | Files | Refs | Submodules | README | LICENSE

ws2811.v (15067B)


      1 /*
      2  * Copyright 2022 Gerd Beuster (gerd@frombelow.net). This is free
      3  * soft-/hardware under the GNU GPL v3 license or any later
      4  * version. See COPYING in the root directory for details.
      5  */
      6 
      7 `ifndef ws2811_v
      8  `define ws2811_v
      9 
     10 // Module led_monitor uses a number of sub-modules:
     11 //
     12 // - Module vram listens to the CPU internals and the busses in order
     13 //   to keep track of the system state, including memory value. (System
     14 //   memory is not tapped but mirrored by listening to the busses and
     15 //   write operations.)
     16 //
     17 // - Module bit2pixel converts 3 bit GRB data to 24 bits GRB data
     18 //   suitable for the WS2811 LED matrix.
     19 //
     20 // - Module ws2811 sends the sequence of 24 bits GRB data to the LED
     21 //   matrix in WS2811 protocol.
     22 
     23 module led_monitor(input        fast_clk,
     24 		   // Memory content is mirrored for monitoring
     25 		   // purposes by listening to data and address bus
     26                    input [7:0] 	data_bus,
     27                    input [7:0] 	address_bus,
     28                    input 	mem_set,
     29 		   // Input of CPU debugging output
     30                    input [15:0] control_logic,
     31                    input [7:0] 	reg_a,
     32                    input [7:0] 	alu,
     33                    input [7:0] 	pc,
     34                    input [7:0] 	dp,
     35 		   // Data line of WS2811 matrix
     36                    output 	ws2811_dout);
     37 
     38    // Each row of the 16x16 matrix represents two bytes,
     39    // therefore the size of the video RAM of the matrix
     40    // is 32 bytes
     41    parameter vram_size = 32;
     42    // WS2811 timing
     43    parameter cycles_t0h =      4; // 0: Cycles for high
     44    parameter cycles_t0l =     13; // 0: Cycles for low
     45    parameter cycles_t1h =     13; // 1: Cycles for high
     46    parameter cycles_t1l =      4; // 1: Cycles for low
     47    parameter cycles_reset =  4000; // Cycles for rest
     48 
     49    // The output of the video RAM is fed into bit2pixel in order to
     50    // convert video RAM data into pixel values for the LED matrix.
     51    wire [2:0]                   vram_dout;
     52    // vram_next requests the next bit from video ram. Video ram sets
     53    // vram_eod when video ram transfer is completed.
     54    wire                         vram_next, vram_eod;
     55    vram #(.size(vram_size))
     56    vram_mem (.din(data_bus),
     57              .ain(address_bus),
     58              .mem_set(mem_set),
     59              .control_logic(control_logic),
     60              .reg_a(reg_a),
     61              .alu(alu),
     62              .pc(pc),
     63              .dp(dp),
     64              .vclk(fast_clk),
     65              .veod(vram_eod), .vnext(vram_next),
     66              .vout(vram_dout));
     67    // bs_next requests to set bs_dout to the next pixel. bs_eod
     68    // indicates that all pixels have been transmitted.
     69    wire                         bs_next, bs_dout, bs_eod;
     70    bit2pixel b2p (.clk(fast_clk),
     71                   .vram_din(vram_dout),  .vram_eod(vram_eod),
     72                   .vram_next(vram_next),
     73                   .dout_next(bs_next), .dout(bs_dout),
     74                   .dout_eod(bs_eod));
     75    // ws2811 translates the bit values of pixels to the high/low
     76    // signals of the WS2811 protocol.
     77    ws2811 #(.cycles_t0h(cycles_t0h), .cycles_t0l(cycles_t0l),
     78             .cycles_t1h(cycles_t1h), .cycles_t1l(cycles_t1l),
     79             .cycles_reset(cycles_reset))
     80    ws (.clk(fast_clk),
     81        .din(bs_dout),
     82        .next(bs_next),
     83        .eod(bs_eod), .dout(ws2811_dout));
     84 endmodule
     85 
     86 // The input interface of the VRAM connects to the databus, the output
     87 // interface to WS2811. The output interface is not random access but
     88 // provides a bitstream.
     89 module vram(// VRAM input interfaces to read internal state
     90             input [7:0]      din,
     91             input [7:0]      ain,
     92             input            mem_set, // Write din to memory cell
     93             input [15:0]     control_logic, // All control logic flags
     94             input [7:0]      reg_a,
     95             input [7:0]      alu,
     96             input [7:0]      pc,
     97             input [7:0]      dp,
     98             // VRAM interface for video output
     99             input            vclk,
    100             input            vnext, // Dump next bit
    101             output reg       veod,
    102             output reg [2:0] vout // Stream output
    103             );
    104 
    105    parameter size = 32;
    106 
    107    reg [7:0]                 mem [0:size-1];
    108    reg [$clog2(size)-1:0]    v_byte_ptr = 0;
    109    reg [2:0]                 v_bit_ptr = 0;
    110    reg [7:0]                 byte_out;
    111 
    112    // The LED matrix is wired in snake wiring, ain_snake allows us to
    113    // access memory cells in the usual left-to-right, row-by-row
    114    // order. We still have to remember that the order of LEDs in the
    115    // memory cells in every other row is reversed.
    116    wire [7:0]                ain_snake;
    117    assign ain_snake = (((ain % 4) == 0) ?
    118                        (ain + 9)
    119                        : (((ain % 4) == 1) ?
    120                           (ain + 7)
    121                           : (ain + 8)));
    122    wire [$clog2(size)-1:0]   v_byte_ptr_snake;
    123    assign v_byte_ptr_snake = (((v_byte_ptr % 4) == 0) ?
    124                               (v_byte_ptr - 7)
    125                               : (((v_byte_ptr % 4) == 1) ?
    126                                  (v_byte_ptr - 9)
    127                                  : (v_byte_ptr - 8)));
    128 
    129    // When using BRAM, we can only write one cell on each
    130    // cycle. Therefore we have to split VRAM updates into a number of
    131    // cycles. The output will only be accurate if the bus clock is
    132    // much slower than the VRAM clock that all VRAM parts are updated
    133    // in one bus clock cycle.
    134  `define state_update_control_logic_0 0
    135  `define state_update_control_logic_1 1
    136  `define state_update_address_bus     2
    137  `define state_update_data_bus        3
    138  `define state_update_accumulator     4
    139  `define state_update_alu             5
    140  `define state_update_pc              6
    141  `define state_update_dp              7
    142  `define state_update_mem             8
    143    reg [3:0]                 state = `state_update_control_logic_0;
    144    always @(posedge(vclk))
    145      case (state)
    146        `state_update_control_logic_0:
    147          begin
    148             mem[0] <= control_logic[7:0];
    149             state <= `state_update_control_logic_1;
    150          end
    151        `state_update_control_logic_1:
    152          begin
    153             mem[1] <= { 1'b0, control_logic[15:8] };
    154             state <= `state_update_address_bus;
    155          end
    156        `state_update_address_bus:
    157          begin
    158             mem[2] <= { ain[0], ain[1], ain[2], ain[3], ain[4], ain[5], ain[6], ain[7] };
    159             state <= `state_update_data_bus;
    160          end
    161        `state_update_data_bus:
    162          begin
    163             mem[3] <= { din[0], din[1], din[2], din[3], din[4], din[5], din[6], din[7] };
    164             state <= `state_update_accumulator;
    165          end
    166        `state_update_alu:
    167          begin
    168             mem[4] <= alu;
    169             state <= `state_update_pc;
    170          end
    171        `state_update_accumulator:
    172          begin
    173             mem[5] <= reg_a;
    174             state <= `state_update_alu;
    175          end
    176        `state_update_pc:
    177          begin
    178             mem[6] <= { pc[0], pc[1], pc[2], pc[3], pc[4], pc[5], pc[6], pc[7] };
    179             state <= `state_update_dp;
    180          end
    181        `state_update_dp:
    182          begin
    183             mem[7] <= { dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7] };
    184             state <= `state_update_mem;
    185          end
    186        `state_update_mem:
    187          begin
    188             if (mem_set && (ain <= (size - 9)))
    189               // We are in the phase for updating memory, and
    190               // the memory in the range shown is written. Therefore we update it.
    191               if ((ain % 4) > 1)
    192                 // While ain_snake de-mangles the order of memory cells, we still have
    193                 // to reverse the bit order of the memory cells in every other row due
    194                 // to the snake wiring.
    195                 mem[ain_snake] <= {din[0], din[1], din[2], din[3],
    196                                    din[4], din[5], din[6], din[7] };
    197               else
    198                 mem[ain_snake] <= din;
    199             state <= `state_update_control_logic_0;
    200          end
    201      endcase
    202 
    203 
    204    // Output interface: Dump memory as bitstream for output on LED array.
    205    always @(posedge(vclk))
    206      begin
    207         byte_out <= mem[v_byte_ptr];
    208         vout <= ((v_byte_ptr < 2) ?
    209                  // First row shows bits of control logic
    210                  ((v_bit_ptr % 2 == 0) ?
    211                   // Alternate colors when showing control logic bits
    212                   { 1'b0, byte_out[v_bit_ptr], 1'b0 }
    213                   : { 1'b0, 1'b0 , byte_out[v_bit_ptr] })
    214                  : ((v_byte_ptr < 8) ?
    215                     // Registers & busses
    216                     ((v_byte_ptr % 2 == 0) ?
    217                      // In alternating colors
    218                      { 1'b0, byte_out[v_bit_ptr], 1'b0 }
    219                      : { 1'b0, 1'b0, byte_out[v_bit_ptr]  })
    220                     :
    221                     // Memory content shown in blue. In order to
    222                     // indicate the location of the PC and the DP,
    223                     // non-set bits of the memory cell pointed to by
    224                     // the PC and DP are colored in the respective
    225                     // color.
    226                     { byte_out[v_bit_ptr],
    227                       ((v_byte_ptr_snake == pc) && (byte_out[v_bit_ptr] == 1'b0)) ? 1'b1 : 1'b0,
    228                       ((v_byte_ptr_snake == dp) && (byte_out[v_bit_ptr] == 1'b0)) ? 1'b1 : 1'b0
    229                       }));
    230         veod <= ((v_bit_ptr == 7) && (v_byte_ptr == (size - 1)));
    231         // Advance to next output bit
    232         if (vnext)
    233           begin
    234              // Memory is organized as bytes, so we may have to
    235              // advance to the next byte when all bits of the current
    236              // bit have been output.
    237              // Note that we do not set v_byte_ptr and v_bit_ptr explicitly
    238              // back to 0 when a complete byte has been output; we simply let
    239              // it overflow.
    240              if (v_bit_ptr == 7)
    241                   v_byte_ptr <= v_byte_ptr + 1;
    242              v_bit_ptr <= v_bit_ptr + 1;
    243           end
    244      end // always @ (posedge(vclk))
    245 endmodule
    246 
    247 // Takes a stream of bit triples representing 3 bit GRB color values.
    248 // These triplets are translated to a 24 bit GRB value as input for
    249 // the ws2811 input stream by module ws2811.
    250 module bit2pixel(input clk,
    251                  // Interface to VRAM
    252                  input [2:0] vram_din,
    253                  input       vram_eod,
    254                  output reg  vram_next,
    255                  // Interface to WS2811
    256                  output reg  dout,
    257                  output reg  dout_eod,
    258                  input       dout_next);
    259 
    260    parameter LED_INTENSITY = 3; // Intensity of LED; range [1..7]
    261    reg                       init = 0;
    262    // Color of output bit
    263    reg [1:0]                 dout_color;
    264    // Position of output bit in output byte
    265    reg [2:0]                 dout_bit;
    266 
    267    always @(posedge(clk))
    268      if (init == 0)
    269        begin
    270           init <= 1;
    271           dout_eod <= 0;
    272           dout <= 0;
    273           dout_color <= 0;
    274           dout_bit <= 0;
    275        end
    276      else
    277        begin
    278           // Each input bit is translated to 3 colors of 8 bits
    279           // intensity. We output 0 unless we output the desired color
    280           // in the desired intensity.
    281           dout <= (dout_bit == 8-LED_INTENSITY) ? vram_din[dout_color] : 0;
    282           dout_eod <= ((vram_eod) && (dout_color == 2) &&
    283                        (dout_bit == 7));
    284           if (dout_next)
    285             begin
    286                if (dout_bit == 7)
    287                  // 8 bit have been output. Switch to next color
    288                  // channel.
    289                  if(dout_color == 2)
    290                    begin
    291                       dout_color <= 0;
    292                       // Since all colors have been output, the output
    293                       // for this bit from VRAM is complete and we
    294                       // request the next one.
    295                       vram_next <= 1;
    296                    end
    297                  else
    298                    dout_color <= dout_color + 1;
    299                // Since width of dout_bit is 3 bit, it runs
    300                // over bits 0..7
    301                dout_bit <= dout_bit + 1;
    302             end // if (dout_next)
    303           else
    304             vram_next <= 0;
    305        end
    306 
    307 endmodule
    308 
    309 module ws2811(input clk, input din, input eod, output reg next, output reg dout);
    310 
    311    // One clock cycle @ 12 Mhz:
    312    // 83.3333 ns
    313 
    314    // Protocol
    315 
    316    // https://www.tme.eu/Document/26d574b43ad9ddaffa4d5bcd140ec145/WS2811.pdf
    317    // Send order: GRB, MSB First
    318    // 0: High 220 ns ~  380 ns | Low 580 ns ~ 1600 ns
    319    // 1: High 580 ns ~ 1600 ns | Low 220 ns ~  420 ns
    320    // Frame length: 800 ns ~ 1980 ns
    321    // Reset: > 280000 ns
    322    parameter cycles_t0h =      4; // 0: Cycles for high
    323    parameter cycles_t0l =     13; // 0: Cycles for low
    324    parameter cycles_t1h =     13; // 1: Cycles for high
    325    parameter cycles_t1l =      4; // 1: Cycles for low
    326    parameter cycles_reset =  4000; // Cycles for rest
    327 
    328  `define state_reset 0
    329  `define state_high 1
    330  `define state_low 2
    331    reg [1:0]           state_ptr;
    332    reg [15:0]          cycles_counter;
    333 
    334    reg                 init = 0;
    335 
    336    always @(posedge(clk))
    337      if (init == 0)
    338        begin
    339           init <= 1;
    340           state_ptr <= `state_reset;
    341 	      cycles_counter <= cycles_reset;
    342 	      dout <= 0;
    343           next <= 0;
    344        end
    345      else
    346        begin
    347           cycles_counter <= cycles_counter + 1;
    348           next <= 0;
    349           // In state_reset, dout is kept low in order to initate
    350           // a new transmission of color values for all LEDs. After
    351           // that, we alternate between state_high and state_low,
    352           // sending each color value bit by PWM. Each cycle
    353           // represents a bit: The length of the high/low phases
    354           // depends on the bit value.
    355           case (state_ptr)
    356             `state_reset:
    357               if (cycles_counter == cycles_reset)
    358                 begin
    359                    state_ptr <= `state_high;
    360                    cycles_counter <= 0;
    361                    dout <= 1;
    362                 end
    363             `state_high:
    364               if (cycles_counter == ((din == 1'b1) ?
    365                                      cycles_t1h-1 : cycles_t0h-1))
    366                 begin
    367                    state_ptr <= `state_low;
    368                    cycles_counter <= 0;
    369                    dout <= 0;
    370                 end
    371             `state_low:
    372               if (cycles_counter == ((din == 1'b1) ?
    373                                      cycles_t1l-1 : cycles_t0l-1))
    374                 begin
    375                    if (eod)
    376                      // When all bits are send, we are done and start over.
    377                      begin
    378                         state_ptr <= `state_reset;
    379                         cycles_counter <= 0;
    380                         dout <= 0;
    381                      end
    382                    else
    383                      // Transmit next bit
    384                      begin
    385                         state_ptr <= `state_high;
    386                         cycles_counter <= 0;
    387                         dout <= 1;
    388                      end // else: !if(eod)
    389                    next <= 1;
    390                 end
    391           endcase // case (state_ptr)
    392        end
    393 
    394 endmodule
    395 
    396 `endif