mirror of
https://github.com/pConst/basic_verilog.git
synced 2025-01-14 06:42:54 +08:00
113 lines
3.2 KiB
Systemverilog
113 lines
3.2 KiB
Systemverilog
//------------------------------------------------------------------------------
|
|
// cdc_strobe.sv
|
|
// Konstantin Pavlov, pavlovconst@gmail.com
|
|
//------------------------------------------------------------------------------
|
|
|
|
// INFO ------------------------------------------------------------------------
|
|
// Clock crossing setup for single-pulse strobes
|
|
// Strobes could trigger transfers of almost-static data between clock doamins
|
|
//
|
|
// - Maximum input strobe rate is every second clk1 clock cycle
|
|
//
|
|
// - Input strobe may span several clock cycles, but it will be considered one
|
|
// event and only one single-cycle strobe will be generated to the output
|
|
//
|
|
// - When clk2 is essentially less than clk1 it is possible that strb2 will
|
|
// remain HIGH for several consecutive clk2 cycles. On the output every
|
|
// HIGH cycle should be considered as a separate strobe event
|
|
//
|
|
// - When clk2 is essentially less than clk1 - output strobes could even
|
|
// "overlap" or miss. In this case, please restrict input strobe event rate
|
|
//
|
|
// - cdc_strobe module features a 2 clock cycles propagation delay
|
|
//
|
|
//
|
|
//
|
|
// False_path constraint is required from all nodes with "_FP_ATTR" suffix
|
|
//
|
|
// For Quartus:
|
|
// set_false_path -from [get_registers {*_FP_ATTR*}]
|
|
//
|
|
// For Vivado:
|
|
// set_false_path -from [get_cells -hier -filter {NAME =~ *_FP_ATTR*}]
|
|
//
|
|
|
|
|
|
/* --- INSTANTIATION TEMPLATE BEGIN ---
|
|
|
|
cdc_strobe cdc_wr_req (
|
|
.arst( 1'b0 ),
|
|
|
|
.clk1( clk1 ),
|
|
.nrst1( 1'b1 ),
|
|
.strb1( wr_req_clk1 ),
|
|
|
|
.clk2( {8{clk2}} ),
|
|
.nrst2( 1'b1 ),
|
|
.strb2( wr_req_clk2 )
|
|
);
|
|
|
|
--- INSTANTIATION TEMPLATE END ---*/
|
|
|
|
|
|
module cdc_strobe (
|
|
input arst, // async reset
|
|
|
|
input clk1, // clock domain 1 clock
|
|
input nrst1, // clock domain 1 reset (inversed)
|
|
input strb1, // clock domain 1 strobe
|
|
|
|
input clk2, // clock domain 2 clock
|
|
input nrst2, // clock domain 2 reset (inversed)
|
|
output strb2 // clock domain 2 strobe
|
|
);
|
|
|
|
// buffering strb1
|
|
logic strb1_b = 1'b0;
|
|
always @(posedge clk1 or posedge arst) begin
|
|
if( arst ) begin
|
|
strb1_b <= '0;
|
|
end else if( ~nrst1 ) begin // Quartus demands to split these if conditions
|
|
strb1_b <= '0;
|
|
end else begin
|
|
strb1_b <= strb1;
|
|
end
|
|
end
|
|
|
|
// strb1 edge detector
|
|
// prevents secondary strobe generation in case strb1 is not one-cycle-high
|
|
logic strb1_ed;
|
|
assign strb1_ed = ( ~strb1_b && strb1 );
|
|
|
|
// 2 bit gray counter, it must NEVER be reset
|
|
logic [1:0] gc_FP_ATTR = '0;
|
|
always @(posedge clk1 or posedge arst) begin
|
|
if( arst ) begin
|
|
// nop
|
|
end else begin
|
|
if( strb1_ed ) begin
|
|
gc_FP_ATTR[1:0] <= {gc_FP_ATTR[0],~gc_FP_ATTR[1]}; // incrementing counter
|
|
end
|
|
end
|
|
end
|
|
|
|
// buffering counter value on clk2
|
|
// gray counter doesnt need a synchronizer
|
|
logic [1:0][1:0] gc_b = '0;
|
|
always @(posedge clk2 or posedge arst) begin
|
|
if( arst ) begin
|
|
gc_b[1:0] <= {2{gc_FP_ATTR[1:0]}};
|
|
end else if( ~nrst2 ) begin // Quartus demands to split these if conditions
|
|
gc_b[1:0] <= {2{gc_FP_ATTR[1:0]}};
|
|
end else begin
|
|
gc_b[1:0] <= {gc_b[0],gc_FP_ATTR[1:0]}; // shifting left
|
|
end
|
|
end
|
|
|
|
// gray_bit_b edge detector
|
|
assign strb2 = ( gc_b[1][1:0] != gc_b[0][1:0] );
|
|
|
|
|
|
endmodule
|
|
|