独立的 Nexys 4 时钟随时间去同步化
Independent Nexys 4 clocks desynchronizing over time
我们正在开发一个程序,需要两个设备上的同步时钟来测量超声波信号的飞行时间。
问题在于,当我们综合程序并在两个独立的 Nexys4 FPGA 上对其进行测试时,距离会随着时间的推移而减小 (0.13 cm/s)。这个比率是恒定的,并且一直在下降,导致我们认为问题出在代码中。
并且当我们只在一台 Nexys 4 中合成程序时,随着时间的推移没有看到减少。
我们有一个模块来监听信号(每个从机 3 个信号,3 个从机),称为 dataListener:信号 SendCommand 是 UART 模块的控制信号,它向 SRF02 超声波发送方向和命令,这个模块在两个设备上完全相同。
module dataListener(
input mclk,
input clkSync,
input reset,
input rxDataRdy,
output wire[7:0] command,
output reg[7:0] direction,
output reg read,
output reg sendCommand,
output reg dataChanged,
output reg [1:0] slave,
output reg [1:0] sensor
);
parameter dir0 = 8'd0;
parameter dir1 = 8'd3;
parameter dir2 = 8'd6;
parameter rangingCommand = 8'd87;
parameter readCommand = 8'd94;
//parameter clkTime = 0.000000001; // 1ns Simulation // 10ns FPGA
//parameter windowTime = 0.08; // 80 ms
//parameter listenTime = 0.07; // 70ms
parameter windowCyclesDuration = 8000000;
parameter listenCyclesDuration = 7000000;
reg [54:0] windowCounter;
reg emitSent;
reg readSent;
reg slave1;
reg slave2;
reg slave3;
assign command = emitSent ? readCommand : rangingCommand;
///////////////////////////////////////////////////////////////////
always @(posedge mclk) begin
if( reset )begin
sensor <= 2'b0;
windowCounter <= 55'b0;
emitSent <= 0;
readSent <= 0;
slave <=0;
end else begin
if( clkSync ) begin
if( windowCounter >= windowCyclesDuration )begin //Window ended
windowCounter <= 55'b0; //resetCounter
emitSent <= 0;
readSent <= 0;
if( sensor == 2'd2 )begin
sensor <= 2'b0;
if(slave == 2'd2)
slave <= 2'b0;
else
slave <= slave+1'b1;
end else begin
sensor <= sensor + 1'b1;
end
end else begin
windowCounter <= windowCounter + 1'b1; //Window in process
if(!emitSent)begin
sendCommand <= 1;
end
else if( (windowCounter >= listenCyclesDuration) && !readSent)begin //listen done, time to send the read command
sendCommand <= 1;
end
end
if(sendCommand)begin
sendCommand <= 0; //Shut down "sendCommand" signal.
if(!emitSent)
emitSent <= 1;
else
readSent <= 1;
end
end
/// Process incoming data
if( rxDataRdy )begin
read <= 1;
end else if( read )begin
read <= 0;
end
end
end
//////////////////////////////////////////////////////////////////
always @( sensor ) begin
case(sensor)
2'd0: begin
direction <= dir0;
end
2'd1: begin
direction <= dir1;
end
2'd2: begin
direction <= dir2;
end
default: begin
direction <= dir0;
end
endcase
end
endmodule
从设备上发送命令的模块:
module slave(
input mclk,
input clkSync,
input reset,
output [7:0] command,
output [7:0] direction,
output reg sendCommand,
output inWindow
);
parameter numSlave = 2'b0; //Between 0-2
parameter dir=8'd0; //Depends on the slaves direction
parameter comm=8'd92;
assign command = comm;
assign direction = dir;
parameter windowCyclesDuration = 8000000;
reg [54:0] windowCounter;
reg [1:0] sensor, slave;
reg commandSent;
assign inWindow = (slave == numSlave);
always @(posedge mclk) begin
if( reset )begin
windowCounter <= 55'b0;
sendCommand <=0;
commandSent <= 1;
slave <= 2'b0;
sensor <= 2'b0;
end else begin
if( clkSync ) begin
if( windowCounter >= windowCyclesDuration )begin //Window ended
windowCounter <= 55'b0; //resetCounter
commandSent <= 0;
if( sensor == 2'd2 )begin
sensor <= 2'b0;
if(slave == 2'd2)
slave <= 2'b0;
else
slave <= slave + 1'b1;
end else begin
sensor <= sensor + 1'b1;
end
end else begin
windowCounter <= windowCounter + 1'b1; //Window in process
if( inWindow && !commandSent)begin //im in my window and command not sent yet
sendCommand <= 1;//send when a new window is about to begin
commandSent <= 1;
end
end
if(sendCommand)begin
sendCommand <= 0; //Shut down "sendCommand" signal.
end
end
end
end
endmodule
信号 clkSync
仅在两个设备都处于 'Sync' 时激活,这仅发生在通过电缆开始运行时,然后将电缆移除以允许移动。
这里是master的同步模块:
module SyncM(
input mclk,
input reset,
input response1,
input response2,
input response3,
output reg call1,
output reg call2,
output reg call3,
output reg clkSync,
output reg slave1,
output reg slave2,
output reg slave3
);
always @ (posedge mclk) begin
if(reset)begin
clkSync <= 0;
slave1 <= 0;
slave2 <= 0;
slave3 <= 0;
call1 <= 0;
call2 <= 0;
call3 <= 0;
end else begin
if( btn && !call1 )begin
call1 <= 1;
call2 <= 1;
call3 <= 1;
clkSync <= 1;
end
if(response1)
slave1 <= 1;
if(response2)
slave2 <= 1;
if(response3)
slave3 <= 1;
end
end
endmodule
和从机同步模块,call
信号通过电缆从主机发送到从机。
`timescale 1ns / 1ps
module SyncS(
input reset,
input call,
output reg clkSync,
output reg response
);
always @ (reset or call) begin
if(reset) begin
clkSync <= 0;
response <= 0;
end else begin
if (call) begin
response <= 1;
clkSync <= 1;
end
end
end
endmodule
我还没有完全理解你的代码。但是,问题似乎是,您依赖于 FPGA 板上的外部振荡器。如果你使用两块板,两个振荡器将不会 运行 在完全相同的频率上。因此,如果您在启动后仅补偿一次相移,时钟将在一段时间后不同步。这就是它只适用于一块板的原因。
有两种可能的解决方案:
只使用一个时钟源(振荡器)并将时钟转发到另一块板。
定期补偿相移。
这两种解决方案都需要两块板之间或多或少稳定的连接。
我们正在开发一个程序,需要两个设备上的同步时钟来测量超声波信号的飞行时间。
问题在于,当我们综合程序并在两个独立的 Nexys4 FPGA 上对其进行测试时,距离会随着时间的推移而减小 (0.13 cm/s)。这个比率是恒定的,并且一直在下降,导致我们认为问题出在代码中。
并且当我们只在一台 Nexys 4 中合成程序时,随着时间的推移没有看到减少。
我们有一个模块来监听信号(每个从机 3 个信号,3 个从机),称为 dataListener:信号 SendCommand 是 UART 模块的控制信号,它向 SRF02 超声波发送方向和命令,这个模块在两个设备上完全相同。
module dataListener(
input mclk,
input clkSync,
input reset,
input rxDataRdy,
output wire[7:0] command,
output reg[7:0] direction,
output reg read,
output reg sendCommand,
output reg dataChanged,
output reg [1:0] slave,
output reg [1:0] sensor
);
parameter dir0 = 8'd0;
parameter dir1 = 8'd3;
parameter dir2 = 8'd6;
parameter rangingCommand = 8'd87;
parameter readCommand = 8'd94;
//parameter clkTime = 0.000000001; // 1ns Simulation // 10ns FPGA
//parameter windowTime = 0.08; // 80 ms
//parameter listenTime = 0.07; // 70ms
parameter windowCyclesDuration = 8000000;
parameter listenCyclesDuration = 7000000;
reg [54:0] windowCounter;
reg emitSent;
reg readSent;
reg slave1;
reg slave2;
reg slave3;
assign command = emitSent ? readCommand : rangingCommand;
///////////////////////////////////////////////////////////////////
always @(posedge mclk) begin
if( reset )begin
sensor <= 2'b0;
windowCounter <= 55'b0;
emitSent <= 0;
readSent <= 0;
slave <=0;
end else begin
if( clkSync ) begin
if( windowCounter >= windowCyclesDuration )begin //Window ended
windowCounter <= 55'b0; //resetCounter
emitSent <= 0;
readSent <= 0;
if( sensor == 2'd2 )begin
sensor <= 2'b0;
if(slave == 2'd2)
slave <= 2'b0;
else
slave <= slave+1'b1;
end else begin
sensor <= sensor + 1'b1;
end
end else begin
windowCounter <= windowCounter + 1'b1; //Window in process
if(!emitSent)begin
sendCommand <= 1;
end
else if( (windowCounter >= listenCyclesDuration) && !readSent)begin //listen done, time to send the read command
sendCommand <= 1;
end
end
if(sendCommand)begin
sendCommand <= 0; //Shut down "sendCommand" signal.
if(!emitSent)
emitSent <= 1;
else
readSent <= 1;
end
end
/// Process incoming data
if( rxDataRdy )begin
read <= 1;
end else if( read )begin
read <= 0;
end
end
end
//////////////////////////////////////////////////////////////////
always @( sensor ) begin
case(sensor)
2'd0: begin
direction <= dir0;
end
2'd1: begin
direction <= dir1;
end
2'd2: begin
direction <= dir2;
end
default: begin
direction <= dir0;
end
endcase
end
endmodule
从设备上发送命令的模块:
module slave(
input mclk,
input clkSync,
input reset,
output [7:0] command,
output [7:0] direction,
output reg sendCommand,
output inWindow
);
parameter numSlave = 2'b0; //Between 0-2
parameter dir=8'd0; //Depends on the slaves direction
parameter comm=8'd92;
assign command = comm;
assign direction = dir;
parameter windowCyclesDuration = 8000000;
reg [54:0] windowCounter;
reg [1:0] sensor, slave;
reg commandSent;
assign inWindow = (slave == numSlave);
always @(posedge mclk) begin
if( reset )begin
windowCounter <= 55'b0;
sendCommand <=0;
commandSent <= 1;
slave <= 2'b0;
sensor <= 2'b0;
end else begin
if( clkSync ) begin
if( windowCounter >= windowCyclesDuration )begin //Window ended
windowCounter <= 55'b0; //resetCounter
commandSent <= 0;
if( sensor == 2'd2 )begin
sensor <= 2'b0;
if(slave == 2'd2)
slave <= 2'b0;
else
slave <= slave + 1'b1;
end else begin
sensor <= sensor + 1'b1;
end
end else begin
windowCounter <= windowCounter + 1'b1; //Window in process
if( inWindow && !commandSent)begin //im in my window and command not sent yet
sendCommand <= 1;//send when a new window is about to begin
commandSent <= 1;
end
end
if(sendCommand)begin
sendCommand <= 0; //Shut down "sendCommand" signal.
end
end
end
end
endmodule
信号 clkSync
仅在两个设备都处于 'Sync' 时激活,这仅发生在通过电缆开始运行时,然后将电缆移除以允许移动。
这里是master的同步模块:
module SyncM(
input mclk,
input reset,
input response1,
input response2,
input response3,
output reg call1,
output reg call2,
output reg call3,
output reg clkSync,
output reg slave1,
output reg slave2,
output reg slave3
);
always @ (posedge mclk) begin
if(reset)begin
clkSync <= 0;
slave1 <= 0;
slave2 <= 0;
slave3 <= 0;
call1 <= 0;
call2 <= 0;
call3 <= 0;
end else begin
if( btn && !call1 )begin
call1 <= 1;
call2 <= 1;
call3 <= 1;
clkSync <= 1;
end
if(response1)
slave1 <= 1;
if(response2)
slave2 <= 1;
if(response3)
slave3 <= 1;
end
end
endmodule
和从机同步模块,call
信号通过电缆从主机发送到从机。
`timescale 1ns / 1ps
module SyncS(
input reset,
input call,
output reg clkSync,
output reg response
);
always @ (reset or call) begin
if(reset) begin
clkSync <= 0;
response <= 0;
end else begin
if (call) begin
response <= 1;
clkSync <= 1;
end
end
end
endmodule
我还没有完全理解你的代码。但是,问题似乎是,您依赖于 FPGA 板上的外部振荡器。如果你使用两块板,两个振荡器将不会 运行 在完全相同的频率上。因此,如果您在启动后仅补偿一次相移,时钟将在一段时间后不同步。这就是它只适用于一块板的原因。
有两种可能的解决方案:
只使用一个时钟源(振荡器)并将时钟转发到另一块板。
定期补偿相移。
这两种解决方案都需要两块板之间或多或少稳定的连接。