blob: 31f25c16e047f924115d7870c0b059f6f4d6fa31 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// SPDX-FileCopyrightText: 2021 , Dinesh Annayya
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the 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.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileContributor: Created by Dinesh Annayya <dinesha@opencores.org>
//
//////////////////////////////////////////////////////////////////////
//// ////
//// ws281x Register ////
//// ////
//// This file is part of the riscduino cores project ////
//// https://github.com/dineshannayya/riscduino.git ////
//// ////
//// Description ////
//// Manages the 4x ws281x driver register ////
//// ////
//// To Do: ////
//// nothing ////
//// ////
//// Author(s): ////
//// - Dinesh Annayya, dinesha@opencores.org ////
//// ////
//// Revision : ////
//// 0.1 - 23rd Aug 2022, Dinesh A ////
//// initial version ////
//////////////////////////////////////////////////////////////////////
module ws281x_reg #( parameter NP = 2, // Number of PORT
parameter DW = 32, // DATA WIDTH
parameter AW = 4, // ADDRESS WIDTH
parameter BW = 4 // BYTE WIDTH
) (
// System Signals
// Inputs
input logic mclk ,
input logic h_reset_n ,
// Reg Bus Interface Signal
input logic reg_cs ,
input logic reg_wr ,
input logic [AW-1:0] reg_addr ,
input logic [DW-1:0] reg_wdata ,
input logic [BW-1:0] reg_be ,
// Outputs
output logic [DW-1:0] reg_rdata ,
output logic reg_ack ,
output logic[15:0] cfg_reset_period , // Reset period interm of clk
output logic [9:0] cfg_clk_period , // Total bit clock period
output logic [9:0] cfg_th0_period , // bit-0 drive low period
output logic [9:0] cfg_th1_period , // bit-1 drive low period
// wd281x port-0 data
output logic port0_enb ,
input logic port0_rd ,
output logic [23:0] port0_data ,
output logic port0_dval ,
// wd281x port-1 data
output logic port1_enb ,
input logic port1_rd ,
output logic [23:0] port1_data ,
output logic port1_dval
//// wd281x port-2 data
//output logic port2_enb ,
//input logic port2_rd ,
//output logic [23:0] port2_data ,
//output logic port2_dval ,
//// wd281x port-3 data
//output logic port3_enb ,
//input logic port3_rd ,
//output logic [23:0] port3_data ,
//output logic port3_dval
);
//-----------------------------------------------------------------------
// Internal Wire Declarations
//-----------------------------------------------------------------------
logic sw_rd_en ;
logic sw_wr_en ;
logic [AW-1:0] sw_addr ;
logic [DW-1:0] sw_reg_wdata ;
logic [BW-1:0] sw_be ;
logic [DW-1:0] reg_out ;
logic [DW-1:0] reg_0 ;
logic [DW-1:0] reg_1 ;
logic [DW-1:0] reg_2 ;
logic [DW-1:0] reg_3 ;
logic [NP-1:0] fifo_full ;
logic [NP-1:0] fifo_empty ;
logic [NP-1:0] fifo_wr ;
logic [NP-1:0] fifo_rd ;
logic [23:0] fifo_rdata[0:NP-1] ;
logic [NP-1:0] port_op_done ;
assign sw_addr = reg_addr;
assign sw_be = reg_be;
assign sw_rd_en = reg_cs & !reg_wr;
assign sw_wr_en = reg_cs & reg_wr;
assign sw_reg_wdata = reg_wdata;
//-----------------------------------------------------------------------
// register read enable and write enable decoding logic
//-----------------------------------------------------------------------
wire sw_wr_en_0 = sw_wr_en & (sw_addr == 4'h0);
wire sw_wr_en_1 = sw_wr_en & (sw_addr == 4'h1);
wire sw_wr_en_2 = sw_wr_en & (sw_addr == 4'h2);
wire sw_wr_en_3 = sw_wr_en & (sw_addr == 4'h3);
wire sw_wr_en_4 = sw_wr_en & (sw_addr == 4'h4) & !fifo_full[0]; // Write only if fifo is not full
wire sw_wr_en_5 = sw_wr_en & (sw_addr == 4'h5) & !fifo_full[1]; // Write only if fifo is not full
wire sw_wr_en_6 = sw_wr_en & (sw_addr == 4'h6) & !fifo_full[2]; // Write only if fifo is not full
wire sw_wr_en_7 = sw_wr_en & (sw_addr == 4'h7) & !fifo_full[3]; // Write only if fifo is not full
// Generated seperate write enable case to block the reg ack duration when fifo is full
wire sw_wr_en_t = sw_wr_en_0 | sw_wr_en_1 | sw_wr_en_2 | sw_wr_en_3 | sw_wr_en_4 | sw_wr_en_5 | sw_wr_en_6 | sw_wr_en_7;
always @ (posedge mclk or negedge h_reset_n)
begin : preg_out_Seq
if (h_reset_n == 1'b0) begin
reg_rdata <= 'h0;
reg_ack <= 1'b0;
end else if (reg_cs && !reg_ack && sw_rd_en) begin
reg_rdata <= reg_out[DW-1:0] ;
reg_ack <= 1'b1;
end else if (reg_cs && !reg_ack && sw_wr_en_t) begin // Block Ack generation when FIFO is full
reg_ack <= 1'b1;
end else begin
reg_ack <= 1'b0;
end
end
//----------------------------------------
// Hardware Command Register
// Assumption: Maximum 32 port assumed
//----------------------------------------
assign port0_enb = reg_0[0];
assign port1_enb = reg_0[1];
//assign port2_enb = reg_0[2];
//assign port3_enb = reg_0[3];
generic_register #(.WD(4)) u_reg_0(
//List of Inputs
.we ({4{sw_wr_en_0 &
sw_be[0] }}),
.data_in (sw_reg_wdata[3:0]),
.reset_n (h_reset_n ),
.clk (mclk ),
//List of Outs
.data_out (reg_0[3:0] )
);
// CONFIG-0
assign cfg_reset_period = reg_1[15:0];
gen_16b_reg #(32'h0) u_reg_1 (
//List of Inputs
.reset_n (h_reset_n ),
.clk (mclk ),
.cs (sw_wr_en_1 ),
.we (sw_be[1:0] ),
.data_in (sw_reg_wdata[15:0] ),
//List of Outs
.data_out (reg_1[15:0] )
);
// CONFIG-1
assign cfg_th1_period = reg_2[29:20]; // High Exit Period for Data-1
assign cfg_th0_period = reg_2[19:10]; // High Exit period for Data-0
assign cfg_clk_period = reg_2[9:0];
gen_32b_reg #(32'h0) u_reg_2 (
//List of Inputs
.reset_n (h_reset_n ),
.clk (mclk ),
.cs (sw_wr_en_2 ),
.we (sw_be ),
.data_in (sw_reg_wdata ),
//List of Outs
.data_out (reg_2 )
);
assign port0_dval =!fifo_empty[0];
assign port1_dval =!fifo_empty[1];
//assign port2_dval =!fifo_empty[2];
//assign port3_dval =!fifo_empty[3];
assign reg_3 = { 2'b00,fifo_empty[1],fifo_full[1],
2'b00,fifo_empty[0],fifo_full[0]};
//----------------------------------------------------
// DATA FIFO
//----------------------------------------------------
assign fifo_wr[0] = sw_wr_en_4 & reg_ack;
assign fifo_wr[1] = sw_wr_en_5 & reg_ack;
//assign fifo_wr[2] = sw_wr_en_6 & reg_ack;
//assign fifo_wr[3] = sw_wr_en_7 & reg_ack;
assign fifo_rd[0] = port0_rd;
assign fifo_rd[1] = port1_rd;
//assign fifo_rd[2] = port2_rd;
//assign fifo_rd[3] = port3_rd;
assign port0_data = fifo_rdata[0];
assign port1_data = fifo_rdata[1];
//assign port2_data = fifo_rdata[2];
//assign port3_data = fifo_rdata[3];
genvar port;
generate
for (port = 0; $unsigned(port) < NP; port=port+1) begin : gfifo
sync_fifo #(.W(24), .D(8)) u_fifo
(
.clk (mclk ),
.reset_n (h_reset_n ),
.wr_en (fifo_wr[port] ),
.wr_data (sw_reg_wdata[23:0] ),
.full (fifo_full[port] ),
.empty (fifo_empty[port] ),
.rd_en (fifo_rd[port] ),
.rd_data (fifo_rdata[port] )
);
end
endgenerate // gfifo
//-----------------------------------------------------------------------
// Register Read Path Multiplexer instantiation
//-----------------------------------------------------------------------
always_comb
begin
reg_out [31:0] = 32'h0;
case (sw_addr [3:0])
4'b0000 : reg_out [31:0] = reg_0 [31:0];
4'b0001 : reg_out [31:0] = reg_1 [31:0];
4'b0010 : reg_out [31:0] = reg_2 [31:0];
4'b0011 : reg_out [31:0] = reg_3 [31:0];
4'b0100 : reg_out [31:0] = port0_data;
4'b0101 : reg_out [31:0] = port1_data;
// 4'b0110 : reg_out [31:0] = port2_data;
// 4'b0111 : reg_out [31:0] = port3_data;
default : reg_out [31:0] = 32'h0;
endcase
end
endmodule