/******************************************************************************* * Module: cmd_deser * Date:2015-01-12 * Author: Andrey Filippov * Description: Expand command address/data from a byte-wide * * Copyright (c) 2015 Elphel, Inc. * cmd_deser.v is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * cmd_deser.v is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify this Program, or any covered work, by linking or combining it * with independent modules provided by the FPGA vendor only (this permission * does not extend to any 3-rd party modules, "soft cores" or macros) under * different license terms solely for the purpose of generating binary "bitstream" * files and/or simulating the code, the copyright holders of this Program give * you the right to distribute the covered work without those independent modules * as long as the source code for them is available from the FPGA vendor free of * charge, and there is no dependence on any encrypted modules for simulating of * the combined code. This permission applies to you if the distributed code * contains all the components and scripts required to completely simulate it * with at least one of the Free Software programs. *******************************************************************************/ `timescale 1ns/1ps module cmd_deser#( parameter ADDR=0, parameter ADDR_MASK = 'hffff, parameter NUM_CYCLES = 6, parameter ADDR_WIDTH = 16, parameter DATA_WIDTH = 32, parameter ADDR1 = 0, // optional second address parameter ADDR_MASK1 = 0, // optional second mask parameter ADDR2 = 0, // optional third address parameter ADDR_MASK2 = 0, // optional third mask parameter WE_EARLY = 0 // if 1 - we and addr will be valid 1 cycle before data )( input rst, input clk, input srst, // sync reset input [7:0] ad, input stb, output [ADDR_WIDTH-1:0] addr, output [DATA_WIDTH-1:0] data, output [(ADDR_MASK2!=0)?2:((ADDR_MASK1!=0)?1:0):0] we ); localparam WE_WIDTH=(ADDR_MASK2!=0)?3:((ADDR_MASK1!=0)?2:1); generate if (NUM_CYCLES==1) cmd_deser_single # ( .ADDR (ADDR), .ADDR_MASK (ADDR_MASK), .ADDR_WIDTH (ADDR_WIDTH), .DATA_WIDTH (DATA_WIDTH), .ADDR1 (ADDR1), .ADDR_MASK1 (ADDR_MASK1), .ADDR2 (ADDR2), .ADDR_MASK2 (ADDR_MASK2), .WE_WIDTH (WE_WIDTH), .WE_EARLY (WE_EARLY) ) i_cmd_deser_single ( .rst(rst), .clk(clk), .srst(srst), .ad(ad), .stb(stb), .addr(addr), .data(data), .we(we) ); else if (NUM_CYCLES==2) cmd_deser_dual # ( .ADDR(ADDR), .ADDR_MASK(ADDR_MASK), .ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH), .ADDR1 (ADDR1), .ADDR_MASK1 (ADDR_MASK1), .ADDR2 (ADDR2), .ADDR_MASK2 (ADDR_MASK2), .WE_WIDTH (WE_WIDTH), .WE_EARLY (WE_EARLY) ) i_cmd_deser_dual ( .rst(rst), .clk(clk), .srst(srst), .ad(ad), .stb(stb), .addr(addr), .data(data), .we(we) ); else cmd_deser_multi # ( .ADDR(ADDR), .ADDR_MASK(ADDR_MASK), .NUM_CYCLES(NUM_CYCLES), .ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH), .ADDR1 (ADDR1), .ADDR_MASK1 (ADDR_MASK1), .ADDR2 (ADDR2), .ADDR_MASK2 (ADDR_MASK2), .WE_WIDTH (WE_WIDTH), .WE_EARLY (WE_EARLY) ) i_cmd_deser_multi ( .rst(rst), .clk(clk), .srst(srst), .ad(ad), .stb(stb), .addr(addr), .data(data), .we(we) ); endgenerate endmodule module cmd_deser_single#( parameter ADDR=0, parameter ADDR_MASK='hffff, parameter ADDR_WIDTH=8, // <=8 parameter DATA_WIDTH=1, // will 0 work? parameter ADDR1=0, parameter ADDR_MASK1=0, parameter ADDR2=0, parameter ADDR_MASK2=0, parameter WE_WIDTH=1, parameter WE_EARLY = 0 // )( input rst, input clk, input srst, // sync reset input [7:0] ad, input stb, output [ADDR_WIDTH-1:0] addr, output [DATA_WIDTH-1:0] data, output [WE_WIDTH-1:0] we ); localparam ADDR_LOW= ADDR & 8'hff; localparam ADDR_MASK_LOW= ADDR_MASK & 8'hff; localparam ADDR_LOW1= ADDR1 & 8'hff; localparam ADDR_MASK_LOW1= ADDR_MASK1 & 8'hff; localparam ADDR_LOW2= ADDR2 & 8'hff; localparam ADDR_MASK_LOW2= ADDR_MASK2 & 8'hff; reg [7:0] deser_r; wire [2:0] match_low; reg [2:0] we_r; assign we = (WE_EARLY > 0)?(match_low[WE_WIDTH-1:0] & {WE_WIDTH{stb}}):we_r[WE_WIDTH-1:0]; assign match_low= { // unused bits will be optimized ((ad ^ ADDR_LOW2) & (8'hff & ADDR_MASK_LOW2)) == 0, ((ad ^ ADDR_LOW1) & (8'hff & ADDR_MASK_LOW1)) == 0, ((ad ^ ADDR_LOW ) & (8'hff & ADDR_MASK_LOW )) == 0}; always @ (posedge rst or posedge clk) begin if (rst) we_r <= 0; else if (srst) we_r <= 0; else we_r <= match_low & {3{stb}}; if (rst) deser_r <= 0; else if (srst) deser_r <= 0; else if ((|match_low) && stb) deser_r <= ad; end assign data={DATA_WIDTH{1'b0}}; // assign addr=deser_r[ADDR_WIDTH-1:0]; assign addr=(WE_EARLY>0) ? ad[ADDR_WIDTH-1:0]: deser_r[ADDR_WIDTH-1:0]; endmodule module cmd_deser_dual#( parameter ADDR=0, parameter ADDR_MASK='hffff, parameter ADDR_WIDTH=12, // <=16 parameter DATA_WIDTH=1, // will 0 work? parameter ADDR1=0, parameter ADDR_MASK1=0, parameter ADDR2=0, parameter ADDR_MASK2=0, parameter WE_WIDTH=1, parameter WE_EARLY = 0 // if 1 - we and addr will be valid 1 cycle before data )( input rst, input clk, input srst, // sync reset input [7:0] ad, input stb, output [ADDR_WIDTH-1:0] addr, output [DATA_WIDTH-1:0] data, output [WE_WIDTH-1:0] we ); localparam ADDR_LOW= ADDR & 8'hff; localparam ADDR_HIGH=(ADDR>>8) & 8'hff; localparam ADDR_MASK_LOW= ADDR_MASK & 8'hff; localparam ADDR_MASK_HIGH=(ADDR_MASK>>8) & 8'hff; localparam ADDR_LOW1= ADDR1 & 8'hff; localparam ADDR_MASK_LOW1= ADDR_MASK1 & 8'hff; localparam ADDR_LOW2= ADDR2 & 8'hff; localparam ADDR_MASK_LOW2= ADDR_MASK2 & 8'hff; localparam ADDR_HIGH1=(ADDR1>>8) & 8'hff; localparam ADDR_MASK_HIGH1=(ADDR_MASK1>>8) & 8'hff; localparam ADDR_HIGH2=(ADDR2>>8) & 8'hff; localparam ADDR_MASK_HIGH2=(ADDR_MASK2>>8) & 8'hff; reg [15:0] deser_r; // reg stb_d; reg [2:0] stb_d; wire [2:0] match_low; wire [2:0] match_high; wire [2:0] we3; reg [2:0] we_r; // assign we=we_r; assign we3 = (WE_EARLY > 0) ? (match_high & stb_d):we_r; // 3 bits wide - for each possible output assign we = we3[WE_WIDTH-1:0]; // truncate assign match_low= {((ad ^ ADDR_LOW2) & (8'hff & ADDR_MASK_LOW2)) == 0, ((ad ^ ADDR_LOW1) & (8'hff & ADDR_MASK_LOW1)) == 0, ((ad ^ ADDR_LOW ) & (8'hff & ADDR_MASK_LOW )) == 0}; assign match_high= {((ad ^ ADDR_HIGH2) & (8'hff & ADDR_MASK_HIGH2)) == 0, ((ad ^ ADDR_HIGH1) & (8'hff & ADDR_MASK_HIGH1)) == 0, ((ad ^ ADDR_HIGH ) & (8'hff & ADDR_MASK_HIGH )) == 0}; always @ (posedge rst or posedge clk) begin if (rst) stb_d <= 3'b0; else if (srst) stb_d <= 3'b0; else stb_d <= stb?match_low:3'b0; if (rst) we_r <= 3'b0; else if (srst) we_r <= 3'b0; else we_r <= match_high & stb_d; if (rst) deser_r[15:0] <= 0; else if (srst) deser_r[15:0] <= 0; else if ((match_low && stb) || (match_high && stb_d)) deser_r[15:0] <= {ad,deser_r[15:8]}; end assign data=0; // {DATA_WIDTH{1'b0}}; // assign addr=deser_r[ADDR_WIDTH-1:0]; assign addr=deser_r[8*WE_EARLY +: ADDR_WIDTH]; endmodule module cmd_deser_multi#( parameter ADDR=0, parameter ADDR_MASK='hffff, parameter NUM_CYCLES=6, // >=3 parameter ADDR_WIDTH=16, parameter DATA_WIDTH=32, parameter ADDR1=0, parameter ADDR_MASK1=0, parameter ADDR2=0, parameter ADDR_MASK2=0, parameter WE_WIDTH=1, parameter WE_EARLY = 0 // if 1 - we and addr will be valid 1 cycle before data )( input rst, input clk, input srst, // sync reset input [7:0] ad, input stb, output [ADDR_WIDTH-1:0] addr, output [DATA_WIDTH-1:0] data, output [WE_WIDTH-1:0] we ); localparam ADDR_LOW= ADDR & 8'hff; localparam ADDR_HIGH=(ADDR>>8) & 8'hff; localparam ADDR_MASK_LOW= ADDR_MASK & 8'hff; localparam ADDR_MASK_HIGH=(ADDR_MASK>>8) & 8'hff; localparam ADDR_LOW1= ADDR1 & 8'hff; localparam ADDR_MASK_LOW1= ADDR_MASK1 & 8'hff; localparam ADDR_LOW2= ADDR2 & 8'hff; localparam ADDR_MASK_LOW2= ADDR_MASK2 & 8'hff; localparam ADDR_HIGH1=(ADDR1>>8) & 8'hff; localparam ADDR_MASK_HIGH1=(ADDR_MASK1>>8) & 8'hff; localparam ADDR_HIGH2=(ADDR2>>8) & 8'hff; localparam ADDR_MASK_HIGH2=(ADDR_MASK2>>8) & 8'hff; reg [8*NUM_CYCLES-1:0] deser_r; reg [2:0] stb_d; wire [2:0] match_low; wire [2:0] match_high; reg [NUM_CYCLES-2:0] sr; reg [NUM_CYCLES-2:0] sr1; reg [NUM_CYCLES-2:0] sr2; wire [2:0] we3; assign we3={sr2[WE_EARLY],sr1[WE_EARLY],sr[WE_EARLY]}; // assign we=sr[WE_WIDTH-1:0]; // we_r; assign we=we3[WE_WIDTH-1:0]; // truncate to required number of bits assign match_low= {((ad ^ ADDR_LOW2) & (8'hff & ADDR_MASK_LOW2)) == 0, ((ad ^ ADDR_LOW1) & (8'hff & ADDR_MASK_LOW1)) == 0, ((ad ^ ADDR_LOW ) & (8'hff & ADDR_MASK_LOW )) == 0}; assign match_high= {((ad ^ ADDR_HIGH2) & (8'hff & ADDR_MASK_HIGH2)) == 0, ((ad ^ ADDR_HIGH1) & (8'hff & ADDR_MASK_HIGH1)) == 0, ((ad ^ ADDR_HIGH ) & (8'hff & ADDR_MASK_HIGH )) == 0}; always @ (posedge rst or posedge clk) begin if (rst) stb_d <= 0; else if (srst) stb_d <= 0; else stb_d <= stb?match_low:3'b0; if (rst) sr <= 0; else if (srst) sr <= 0; else if (match_high[0] && stb_d[0]) sr <= 1 << (NUM_CYCLES-2); else sr <= {1'b0,sr[NUM_CYCLES-2:1]}; if (rst) sr1 <= 0; else if (srst) sr1 <= 0; else if (match_high[1] && stb_d[1]) sr1 <= 1 << (NUM_CYCLES-2); else sr1 <= {1'b0,sr1[NUM_CYCLES-2:1]}; if (rst) sr2 <= 0; else if (srst) sr2 <= 0; else if (match_high[2] && stb_d[2]) sr2 <= 1 << (NUM_CYCLES-2); else sr2 <= {1'b0,sr2[NUM_CYCLES-2:1]}; if (rst) deser_r[8*NUM_CYCLES-1:0] <= 0; else if (srst) deser_r[8*NUM_CYCLES-1:0] <= 0; else if ((match_low && (|stb)) || (match_high && (|stb_d)) || (|sr) || (|sr1) || (|sr2)) deser_r[8*NUM_CYCLES-1:0] <= {ad,deser_r[8*NUM_CYCLES-1:8]}; end assign data=deser_r[DATA_WIDTH+15:16]; // assign addr=deser_r[ADDR_WIDTH-1:0]; assign addr=deser_r[8*WE_EARLY +: ADDR_WIDTH]; endmodule