diff --git a/library/common/ad_upack.v b/library/common/ad_upack.v new file mode 100644 index 000000000..83479d319 --- /dev/null +++ b/library/common/ad_upack.v @@ -0,0 +1,168 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2014 - 2020 (c) Analog Devices, Inc. All rights reserved. +// +// In this HDL repository, there are many different and unique modules, consisting +// of various HDL (Verilog or VHDL) components. The individual modules are +// developed independently, and may be accompanied by separate and unique license +// terms. +// +// The user should read each of these license terms, and understand the +// freedoms and responsibilities that he or she has by using this source/core. +// +// This core 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. +// +// Redistribution and use of source or resulting binaries, with or without modification +// of this file, are permitted under one of the following two license terms: +// +// 1. The GNU General Public License version 2 as published by the +// Free Software Foundation, which can be found in the top level directory +// of this repository (LICENSE_GPL2), and also online at: +// +// +// OR +// +// 2. An ADI specific BSD license, which can be found in the top level directory +// of this repository (LICENSE_ADIBSD), and also on-line at: +// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD +// This will allow to generate bit files and not release the source code, +// as long as it attaches to an ADI device. +// +// *************************************************************************** +// *************************************************************************** + +`timescale 1ns/100ps + +// Unpacker: +// - unpack O_W number of data units from I_W number of data units +// - data unit defined in bits by UNIT_W e.g 8 is a byte +// +// Constraints: +// - O_W <= I_W +// - LATENCY 1 +// - no backpressure +// +// Data format: +// idata [U(I_W-1) .... U(0)] +// odata [U(O_W-1) .... U(0)] +// +// e.g +// I_W = 6 +// O_W = 4 +// UNIT_W = 8 +// +// idata : [B5,B4,B3,B2,B1,B0],[B11,B10,B9,B8,B7,B6] +// odata : [B3,B2,B1,B0],[B7,B6,B5,B4],[B11,B10,B9,B8] +// + +module ad_upack #( + parameter I_W = 4, + parameter O_W = 3, + parameter UNIT_W = 8, + parameter O_REG = 1 +) ( + input clk, + input reset, + input [I_W*UNIT_W-1:0] idata, + input ivalid, + output iready, + + output reg [O_W*UNIT_W-1:0] odata = 'h0, + output reg ovalid = 'b0 +); + +// Width of shift reg is integer multiple of output data width +localparam SH_W = ((I_W/O_W)+1)*O_W; +localparam STEP = I_W % O_W; + +localparam LATENCY = 1; // Minimum input latency from iready to ivalid + +integer i; + +reg [SH_W*UNIT_W-1:0] idata_sh; +reg [SH_W*UNIT_W-1:0] idata_d = 'h0; +reg [SH_W*UNIT_W-1:0] idata_d_nx; +reg [SH_W-1:0] in_use = 'h0; +reg [SH_W-1:0] inmask; + +wire [SH_W-1:0] out_mask = {O_W{1'b1}}; +wire [SH_W-1:0] in_use_nx; +wire [SH_W-1:0] unit_valid; +wire [O_W*UNIT_W-1:0] odata_s; +wire ovalid_s; + +assign unit_valid = (in_use | inmask); +assign in_use_nx = unit_valid >> O_W; + +always @(posedge clk) begin + if (reset) begin + in_use <= 'h0; + end else if (ovalid_s) begin + in_use <= in_use_nx; + end +end + +always @(*) begin + inmask = {I_W{ivalid}}; + if (STEP>0) begin + for (i = STEP; i < O_W; i=i+STEP) begin + if (in_use[i-1]) begin + inmask = {I_W{ivalid}} << i; + end + end + end +end + +always @(*) begin + idata_d_nx = idata_d; + if (ivalid) begin + idata_d_nx = {{(SH_W-I_W)*UNIT_W{1'b0}},idata}; + if (STEP>0) begin + for (i = STEP; i < O_W; i=i+STEP) begin + if (in_use[i-1]) begin + idata_d_nx = (idata << UNIT_W*i) | idata_d; + end + end + end + end +end + +always @(posedge clk) begin + if (ovalid_s) begin + idata_d <= idata_d_nx >> O_W*UNIT_W; + end +end + +assign iready = ~unit_valid[LATENCY*O_W + O_W -1]; + +assign odata_s = idata_d_nx[O_W*UNIT_W-1:0]; +assign ovalid_s = unit_valid[O_W-1]; + +generate + if (O_REG) begin : o_reg + + always @(posedge clk) begin + if (reset) begin + ovalid <= 1'b0; + end else begin + ovalid <= ovalid_s; + end + end + + always @(posedge clk) begin + odata <= odata_s; + end + + end else begin + + always @(*) begin + odata = odata_s; + ovalid = ovalid_s; + end + + end +endgenerate + +endmodule diff --git a/library/common/tb/ad_upack_tb b/library/common/tb/ad_upack_tb new file mode 100755 index 000000000..2b4100471 --- /dev/null +++ b/library/common/tb/ad_upack_tb @@ -0,0 +1,7 @@ +#!/bin/bash + +SOURCE="ad_upack_tb.v" +SOURCE+=" ../ad_upack.v" + +cd `dirname $0` +source run_tb.sh diff --git a/library/common/tb/ad_upack_tb.v b/library/common/tb/ad_upack_tb.v new file mode 100644 index 000000000..420a484c9 --- /dev/null +++ b/library/common/tb/ad_upack_tb.v @@ -0,0 +1,111 @@ +`timescale 1ns/100ps + +module ad_upack_tb; + parameter VCD_FILE = "ad_upack_tb.vcd"; + + parameter I_W = 6; // Width of input channel + parameter O_W = 4; // Width of output channel + parameter UNIT_W = 8; + parameter VECT_W = 1024*8; // Multiple of 8 + + `include "tb_base.v" + + reg [I_W*UNIT_W-1 : 0] idata; + wire [O_W*UNIT_W-1 : 0] odata; + reg ivalid = 'b0; + reg [VECT_W-1:0] input_vector; + reg [VECT_W-1:0] output_vector; + + integer i=0; + integer j=0; + + ad_upack #( + .I_W(I_W), + .O_W(O_W), + .UNIT_W(UNIT_W) + ) DUT ( + .clk(clk), + .reset(reset), + .idata(idata), + .iready(iready), + .ivalid(ivalid), + .odata(odata), + .ovalid(ovalid) + ); + + task test(input no_random); + begin + @(posedge clk); + i = 0; + j = 0; + while (i < VECT_W/(I_W*UNIT_W)) begin + @(posedge clk); + if (iready & (($urandom % 2 == 0) | no_random)) begin + idata <= input_vector[i*(I_W*UNIT_W) +: (I_W*UNIT_W)]; + ivalid <= 1'b1; + i = i + 1; + end else begin + idata <= 'bx; + ivalid <= 1'b0; + end + end + @(posedge clk); + idata <= 'bx; + ivalid <= 1'b0; + + // Check output vector + repeat (20) @(posedge clk); + for (i=0; i<(((VECT_W/(I_W*UNIT_W))*(I_W*UNIT_W))/(O_W*UNIT_W))*(O_W*UNIT_W)/8; i=i+1) begin + if (input_vector[i*8+:8] !== output_vector[i*8+:8]) begin + failed <= 1'b1; + $display("i=%d Expected=%x Found=%x",i,input_vector[i*8+:8],output_vector[i*8+:8]); + end + end + end + endtask + + initial begin + + @(negedge reset); + + // Test with incremental data + for (i=0; i