mirror of
https://github.com/pConst/basic_verilog.git
synced 2025-01-14 06:42:54 +08:00
213 lines
5.9 KiB
Systemverilog
213 lines
5.9 KiB
Systemverilog
//------------------------------------------------------------------------------
|
|
// delay.sv
|
|
// published as part of https://github.com/pConst/basic_verilog
|
|
// Konstantin Pavlov, pavlovconst@gmail.com
|
|
//------------------------------------------------------------------------------
|
|
|
|
// INFO -------------------------------------------------------------------------
|
|
// Static Delay for arbitrary signal, v2
|
|
// Another equivalent names for this module:
|
|
// conveyor.sv
|
|
// synchronizer.sv
|
|
//
|
|
// Tip for Xilinx-based implementations: Leave nrst=1'b1 and ena=1'b1 on
|
|
// purpose of inferring Xilinx`s SRL16E/SRL32E primitives
|
|
//
|
|
// CAUTION: delay module is widely used for synchronizing signals across clock
|
|
// domains. When synchronizing, please exclude input data paths from timing
|
|
// analysis manually by writing appropriate set_false_path SDC constraint
|
|
//
|
|
// Version 2 introduces "ALTERA_BLOCK_RAM" option to implement delays using
|
|
// block RAM. Quartus can make shifters on block RAM automatically
|
|
// using 'altshift_taps' internal module when "Auto Shift Register
|
|
// Replacement" option is ON
|
|
//
|
|
|
|
/* --- INSTANTIATION TEMPLATE BEGIN ---
|
|
|
|
delay #(
|
|
.LENGTH( 2 ),
|
|
.WIDTH( 1 ),
|
|
.TYPE( "CELLS" ),
|
|
.REGISTER_OUTPUTS( "FALSE" )
|
|
) S1 (
|
|
.clk( clk ),
|
|
.nrst( 1'b1 ),
|
|
.ena( 1'b1 ),
|
|
|
|
.in( ),
|
|
.out( )
|
|
);
|
|
|
|
--- INSTANTIATION TEMPLATE END ---*/
|
|
|
|
|
|
module delay #( parameter
|
|
LENGTH = 2, // delay/synchronizer chain length
|
|
WIDTH = 1, // signal width
|
|
|
|
TYPE = "CELLS", // "ALTERA_BLOCK_RAM" infers block ram fifo
|
|
// "ALTERA_TAPS" infers altshift_taps
|
|
// all other values infer registers
|
|
|
|
REGISTER_OUTPUTS = "FALSE", // for block RAM implementations: "TRUE" means that
|
|
// last delay stage will be implemented
|
|
// by means of cell registers to improve timing
|
|
// all other values infer block RAMs only
|
|
|
|
CNTR_W = $clog2(LENGTH)
|
|
)(
|
|
input clk,
|
|
input nrst,
|
|
input ena,
|
|
|
|
input [WIDTH-1:0] in,
|
|
output [WIDTH-1:0] out
|
|
);
|
|
|
|
generate
|
|
|
|
if ( LENGTH == 0 ) begin
|
|
|
|
assign out[WIDTH-1:0] = in[WIDTH-1:0];
|
|
|
|
end else if( LENGTH == 1 ) begin
|
|
|
|
logic [WIDTH-1:0] data = '0;
|
|
always_ff @(posedge clk) begin
|
|
if( ~nrst ) begin
|
|
data[WIDTH-1:0] <= '0;
|
|
end else if( ena ) begin
|
|
data[WIDTH-1:0] <= in[WIDTH-1:0];
|
|
end
|
|
end
|
|
assign out[WIDTH-1:0] = data[WIDTH-1:0];
|
|
|
|
end else begin
|
|
if( TYPE=="ALTERA_BLOCK_RAM" && LENGTH>=3 ) begin
|
|
|
|
logic [WIDTH-1:0] fifo_out;
|
|
logic full;
|
|
logic [CNTR_W-1:0] usedw;
|
|
|
|
logic fifo_out_ena;
|
|
if( REGISTER_OUTPUTS=="TRUE" ) begin
|
|
assign fifo_out_ena = (usedw[CNTR_W-1:0] == LENGTH-1);
|
|
end else begin
|
|
assign fifo_out_ena = full;
|
|
end
|
|
|
|
scfifo #(
|
|
.LPM_WIDTH( WIDTH ),
|
|
.LPM_NUMWORDS( LENGTH ), // must be at least 4
|
|
.LPM_WIDTHU( CNTR_W ),
|
|
.LPM_SHOWAHEAD( "ON" ),
|
|
.UNDERFLOW_CHECKING( "ON" ),
|
|
.OVERFLOW_CHECKING( "ON" ),
|
|
.ENABLE_ECC( "FALSE" ),
|
|
.ALLOW_RWCYCLE_WHEN_FULL( "ON" ),
|
|
.USE_EAB( "ON" )
|
|
) internal_fifo (
|
|
.clock( clk ),
|
|
.aclr( 1'b0 ),
|
|
.sclr( ~nrst ),
|
|
|
|
.data( in[WIDTH-1:0] ),
|
|
.wrreq( ena ),
|
|
.rdreq( ena && fifo_out_ena ),
|
|
|
|
.q( fifo_out[WIDTH-1:0] ),
|
|
.empty( ),
|
|
.full( full ),
|
|
.almost_full( ),
|
|
.almost_empty( ),
|
|
.usedw( usedw[CNTR_W-1:0] ),
|
|
.eccstatus( )
|
|
);
|
|
|
|
logic [WIDTH-1:0] reg_out = '0;
|
|
always_ff @(posedge clk) begin
|
|
if( ~nrst ) begin
|
|
reg_out[WIDTH-1:0] <= '0;
|
|
end else if( ena && fifo_out_ena ) begin
|
|
reg_out[WIDTH-1:0] <= fifo_out[WIDTH-1:0];
|
|
end
|
|
end
|
|
|
|
if( REGISTER_OUTPUTS=="TRUE" ) begin
|
|
assign out[WIDTH-1:0] = reg_out[WIDTH-1:0];
|
|
end else begin
|
|
// avoiding first word fall-through
|
|
assign out[WIDTH-1:0] = (fifo_out_ena)?(fifo_out[WIDTH-1:0]):('0);
|
|
end
|
|
|
|
end else if( TYPE=="ALTERA_TAPS" && LENGTH>=2 ) begin
|
|
|
|
logic [WIDTH-1:0] fifo_out;
|
|
logic [CNTR_W-1:0] delay_cntr = CNTR_W'(LENGTH-1);
|
|
|
|
logic fifo_out_ena;
|
|
assign fifo_out_ena = (delay_cntr[CNTR_W-1:0] == '0);
|
|
|
|
always_ff @(posedge clk) begin
|
|
if( ~nrst ) begin
|
|
delay_cntr[CNTR_W-1:0] <= CNTR_W'(LENGTH-1);
|
|
end else if( ena && ~fifo_out_ena ) begin
|
|
delay_cntr[CNTR_W-1:0] <= delay_cntr[CNTR_W-1:0] - 1'b1;
|
|
end
|
|
end
|
|
|
|
altshift_taps #(
|
|
.intended_device_family( "Cyclone V" ),
|
|
.lpm_hint( "RAM_BLOCK_TYPE=AUTO" ),
|
|
.lpm_type( "altshift_taps" ),
|
|
.number_of_taps( 1 ),
|
|
.tap_distance( (REGISTER_OUTPUTS=="TRUE")?(LENGTH-1):(LENGTH) ), // min. of 3
|
|
.width( WIDTH )
|
|
) internal_taps (
|
|
//.aclr( 1'b0 ),
|
|
//.sclr( ~nrst ),
|
|
.clock( clk ),
|
|
.clken( ena ),
|
|
.shiftin( in[WIDTH-1:0] ),
|
|
.shiftout( fifo_out[WIDTH-1:0] )
|
|
);
|
|
|
|
if( REGISTER_OUTPUTS=="TRUE" ) begin
|
|
logic [WIDTH-1:0] reg_out = '0;
|
|
always_ff @(posedge clk) begin
|
|
if( ~nrst ) begin
|
|
reg_out[WIDTH-1:0] <= '0;
|
|
end else if( ena && fifo_out_ena ) begin
|
|
reg_out[WIDTH-1:0] <= fifo_out[WIDTH-1:0];
|
|
end
|
|
end
|
|
assign out[WIDTH-1:0] = reg_out[WIDTH-1:0];
|
|
end else begin
|
|
assign out[WIDTH-1:0] = fifo_out[WIDTH-1:0];
|
|
end
|
|
|
|
end else begin
|
|
|
|
logic [LENGTH:1][WIDTH-1:0] data = '0;
|
|
always_ff @(posedge clk) begin
|
|
integer i;
|
|
if( ~nrst ) begin
|
|
data <= '0;
|
|
end else if( ena ) begin
|
|
for(i=LENGTH-1; i>0; i--) begin
|
|
data[i+1][WIDTH-1:0] <= data[i][WIDTH-1:0];
|
|
end
|
|
data[1][WIDTH-1:0] <= in[WIDTH-1:0];
|
|
end
|
|
end
|
|
assign out[WIDTH-1:0] = data[LENGTH][WIDTH-1:0];
|
|
|
|
end // if TYPE
|
|
end // if LENGTH
|
|
|
|
endgenerate
|
|
|
|
endmodule
|
|
|